Current File : /home/getxxhzo/app.genicards.com/vendor/laravel/breeze/src/Console/InstallCommand.php |
<?php
namespace Laravel\Breeze\Console;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\PromptsForMissingInput;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;
use RuntimeException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Process\Process;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\multiselect;
use function Laravel\Prompts\select;
class InstallCommand extends Command implements PromptsForMissingInput
{
use InstallsApiStack, InstallsBladeStack, InstallsInertiaStacks;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'breeze:install {stack : The development stack that should be installed (blade,react,vue,api)}
{--dark : Indicate that dark mode support should be installed}
{--pest : Indicate that Pest should be installed}
{--ssr : Indicates if Inertia SSR support should be installed}
{--typescript : Indicates if TypeScript is preferred for the Inertia stack (Experimental)}
{--composer=global : Absolute path to the Composer binary which should be used to install packages}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Install the Breeze controllers and resources';
/**
* Execute the console command.
*
* @return int|null
*/
public function handle()
{
if ($this->argument('stack') === 'vue') {
return $this->installInertiaVueStack();
} elseif ($this->argument('stack') === 'react') {
return $this->installInertiaReactStack();
} elseif ($this->argument('stack') === 'api') {
return $this->installApiStack();
} elseif ($this->argument('stack') === 'blade') {
return $this->installBladeStack();
}
$this->components->error('Invalid stack. Supported stacks are [blade], [react], [vue], and [api].');
return 1;
}
/**
* Install Breeze's tests.
*
* @return bool
*/
protected function installTests()
{
(new Filesystem)->ensureDirectoryExists(base_path('tests/Feature'));
$stubStack = $this->argument('stack') === 'api' ? 'api' : 'default';
if ($this->option('pest') || $this->isUsingPest()) {
if ($this->hasComposerPackage('phpunit/phpunit')) {
$this->removeComposerPackages(['phpunit/phpunit'], true);
}
if (! $this->requireComposerPackages(['pestphp/pest:^2.0', 'pestphp/pest-plugin-laravel:^2.0'], true)) {
return false;
}
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/'.$stubStack.'/pest-tests/Feature', base_path('tests/Feature'));
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/'.$stubStack.'/pest-tests/Unit', base_path('tests/Unit'));
(new Filesystem)->copy(__DIR__.'/../../stubs/'.$stubStack.'/pest-tests/Pest.php', base_path('tests/Pest.php'));
} else {
(new Filesystem)->copyDirectory(__DIR__.'/../../stubs/'.$stubStack.'/tests/Feature', base_path('tests/Feature'));
}
return true;
}
/**
* Install the middleware to a group in the application Http Kernel.
*
* @param string $after
* @param string $name
* @param string $group
* @return void
*/
protected function installMiddlewareAfter($after, $name, $group = 'web')
{
$httpKernel = file_get_contents(app_path('Http/Kernel.php'));
$middlewareGroups = Str::before(Str::after($httpKernel, '$middlewareGroups = ['), '];');
$middlewareGroup = Str::before(Str::after($middlewareGroups, "'$group' => ["), '],');
if (! Str::contains($middlewareGroup, $name)) {
$modifiedMiddlewareGroup = str_replace(
$after.',',
$after.','.PHP_EOL.' '.$name.',',
$middlewareGroup,
);
file_put_contents(app_path('Http/Kernel.php'), str_replace(
$middlewareGroups,
str_replace($middlewareGroup, $modifiedMiddlewareGroup, $middlewareGroups),
$httpKernel
));
}
}
/**
* Determine if the given Composer package is installed.
*
* @param string $package
* @return bool
*/
protected function hasComposerPackage($package)
{
$packages = json_decode(file_get_contents(base_path('composer.json')), true);
return array_key_exists($package, $packages['require'] ?? [])
|| array_key_exists($package, $packages['require-dev'] ?? []);
}
/**
* Installs the given Composer Packages into the application.
*
* @param array $packages
* @param bool $asDev
* @return bool
*/
protected function requireComposerPackages(array $packages, $asDev = false)
{
$composer = $this->option('composer');
if ($composer !== 'global') {
$command = ['php', $composer, 'require'];
}
$command = array_merge(
$command ?? ['composer', 'require'],
$packages,
$asDev ? ['--dev'] : [],
);
return (new Process($command, base_path(), ['COMPOSER_MEMORY_LIMIT' => '-1']))
->setTimeout(null)
->run(function ($type, $output) {
$this->output->write($output);
}) === 0;
}
/**
* Removes the given Composer Packages from the application.
*
* @param array $packages
* @param bool $asDev
* @return bool
*/
protected function removeComposerPackages(array $packages, $asDev = false)
{
$composer = $this->option('composer');
if ($composer !== 'global') {
$command = ['php', $composer, 'remove'];
}
$command = array_merge(
$command ?? ['composer', 'remove'],
$packages,
$asDev ? ['--dev'] : [],
);
return (new Process($command, base_path(), ['COMPOSER_MEMORY_LIMIT' => '-1']))
->setTimeout(null)
->run(function ($type, $output) {
$this->output->write($output);
}) === 0;
}
/**
* Update the "package.json" file.
*
* @param callable $callback
* @param bool $dev
* @return void
*/
protected static function updateNodePackages(callable $callback, $dev = true)
{
if (! file_exists(base_path('package.json'))) {
return;
}
$configurationKey = $dev ? 'devDependencies' : 'dependencies';
$packages = json_decode(file_get_contents(base_path('package.json')), true);
$packages[$configurationKey] = $callback(
array_key_exists($configurationKey, $packages) ? $packages[$configurationKey] : [],
$configurationKey
);
ksort($packages[$configurationKey]);
file_put_contents(
base_path('package.json'),
json_encode($packages, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT).PHP_EOL
);
}
/**
* Delete the "node_modules" directory and remove the associated lock files.
*
* @return void
*/
protected static function flushNodeModules()
{
tap(new Filesystem, function ($files) {
$files->deleteDirectory(base_path('node_modules'));
$files->delete(base_path('yarn.lock'));
$files->delete(base_path('package-lock.json'));
});
}
/**
* Replace a given string within a given file.
*
* @param string $search
* @param string $replace
* @param string $path
* @return void
*/
protected function replaceInFile($search, $replace, $path)
{
file_put_contents($path, str_replace($search, $replace, file_get_contents($path)));
}
/**
* Get the path to the appropriate PHP binary.
*
* @return string
*/
protected function phpBinary()
{
return (new PhpExecutableFinder())->find(false) ?: 'php';
}
/**
* Run the given commands.
*
* @param array $commands
* @return void
*/
protected function runCommands($commands)
{
$process = Process::fromShellCommandline(implode(' && ', $commands), null, null, null, null);
if ('\\' !== DIRECTORY_SEPARATOR && file_exists('/dev/tty') && is_readable('/dev/tty')) {
try {
$process->setTty(true);
} catch (RuntimeException $e) {
$this->output->writeln(' <bg=yellow;fg=black> WARN </> '.$e->getMessage().PHP_EOL);
}
}
$process->run(function ($type, $line) {
$this->output->write(' '.$line);
});
}
/**
* Remove Tailwind dark classes from the given files.
*
* @param \Symfony\Component\Finder\Finder $finder
* @return void
*/
protected function removeDarkClasses(Finder $finder)
{
foreach ($finder as $file) {
file_put_contents($file->getPathname(), preg_replace('/\sdark:[^\s"\']+/', '', $file->getContents()));
}
}
/**
* Prompt for missing input arguments using the returned questions.
*
* @return array
*/
protected function promptForMissingArgumentsUsing()
{
return [
'stack' => fn () => select(
label: 'Which Breeze stack would you like to install?',
options: [
'blade' => 'Blade',
'react' => 'React with Inertia',
'vue' => 'Vue with Inertia',
'api' => 'API only',
]
),
];
}
/**
* Interact further with the user if they were prompted for missing arguments.
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @return void
*/
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output)
{
$stack = $input->getArgument('stack');
if (in_array($stack, ['react', 'vue'])) {
collect(multiselect(
label: 'Would you like any optional features?',
options: [
'dark' => 'Dark mode',
'ssr' => 'Inertia SSR',
'typescript' => 'TypeScript (experimental)',
]
))->each(fn ($option) => $input->setOption($option, true));
} elseif ($stack === 'blade') {
$input->setOption('dark', confirm(
label: 'Would you like dark mode support?',
default: false
));
}
$input->setOption('pest', select(
label: 'Which testing framework do you prefer?',
options: ['PHPUnit', 'Pest'],
default: $this->isUsingPest() ? 'Pest' : 'PHPUnit',
) === 'Pest');
}
/**
* Determine whether the project is already using Pest.
*
* @return bool
*/
protected function isUsingPest()
{
return class_exists(\Pest\TestSuite::class);
}
}