Mercurial > packages > auth
diff src/Console/InstallCommand.php @ 0:90e38de8f2ba
Initial Commit
| author | luka |
|---|---|
| date | Wed, 13 Aug 2025 22:17:20 -0400 |
| parents | |
| children | ac199a7a8931 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Console/InstallCommand.php Wed Aug 13 22:17:20 2025 -0400 @@ -0,0 +1,379 @@ +<?php + +namespace Wizard\Auth\Console; + +use Illuminate\Console\Command; +use Illuminate\Contracts\Console\PromptsForMissingInput; +use Illuminate\Filesystem\Filesystem; +use Illuminate\Support\Arr; +use Illuminate\Support\Str; +use RuntimeException; +use Symfony\Component\Console\Attribute\AsCommand; +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; + +#[AsCommand(name: 'breeze:install')] +class InstallCommand extends Command implements PromptsForMissingInput +{ + use InstallsBladeStack; + + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'breeze:install {stack : The development stack that should be installed (blade)}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Install the Breeze controllers and resources'; + + /** + * Execute the console command. + * + * @return int|null + */ + public function handle() + { + return $this->installBladeStack(); + } + + /** + * Install Breeze's tests. + * + * @return bool + */ + protected function installTests() + { + (new Filesystem)->ensureDirectoryExists(base_path('tests/Feature')); + + $stubStack = match ($this->argument('stack')) { + default => 'default', + }; + + (new Filesystem)->copyDirectory(__DIR__.'/../../stubs/'.$stubStack.'/tests/Feature', base_path('tests/Feature')); + + return true; + } + + /** + * Install the given middleware names into the application. + * + * @param array|string $name + * @param string $group + * @param string $modifier + * @return void + */ + protected function installMiddleware($names, $group = 'web', $modifier = 'append') + { + $bootstrapApp = file_get_contents(base_path('bootstrap/app.php')); + + $names = collect(Arr::wrap($names)) + ->filter(fn ($name) => ! Str::contains($bootstrapApp, $name)) + ->whenNotEmpty(function ($names) use ($bootstrapApp, $group, $modifier) { + $names = $names->map(fn ($name) => "$name")->implode(','.PHP_EOL.' '); + + $stubs = [ + '->withMiddleware(function (Middleware $middleware) {', + '->withMiddleware(function (Middleware $middleware): void {', + ]; + + $bootstrapApp = str_replace( + $stubs, + collect($stubs)->transform(fn ($stub) => $stub + .PHP_EOL." \$middleware->$group($modifier: [" + .PHP_EOL." $names," + .PHP_EOL.' ]);' + .PHP_EOL + )->all(), + $bootstrapApp, + ); + + file_put_contents(base_path('bootstrap/app.php'), $bootstrapApp); + }); + } + + /** + * Install the given middleware aliases into the application. + * + * @param array $aliases + * @return void + */ + protected function installMiddlewareAliases($aliases) + { + $bootstrapApp = file_get_contents(base_path('bootstrap/app.php')); + + $aliases = collect($aliases) + ->filter(fn ($alias) => ! Str::contains($bootstrapApp, $alias)) + ->whenNotEmpty(function ($aliases) use ($bootstrapApp) { + $aliases = $aliases->map(fn ($name, $alias) => "'$alias' => $name")->implode(','.PHP_EOL.' '); + + $stubs = [ + '->withMiddleware(function (Middleware $middleware) {', + '->withMiddleware(function (Middleware $middleware): void {', + ]; + + $bootstrapApp = str_replace( + $stubs, + collect($stubs)->transform(fn ($stub) => $stub + .PHP_EOL.' $middleware->alias([' + .PHP_EOL." $aliases," + .PHP_EOL.' ]);' + .PHP_EOL + )->all(), + $bootstrapApp, + ); + + file_put_contents(base_path('bootstrap/app.php'), $bootstrapApp); + }); + } + + /** + * 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 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 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 dependencies in the "package.json" file. + * + * @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 + ); + } + + /** + * Update the scripts in the "package.json" file. + * + * @return void + */ + protected static function updateNodeScripts(callable $callback) + { + if (! file_exists(base_path('package.json'))) { + return; + } + + $content = json_decode(file_get_contents(base_path('package.json')), true); + + $content['scripts'] = $callback( + array_key_exists('scripts', $content) ? $content['scripts'] : [] + ); + + file_put_contents( + base_path('package.json'), + json_encode($content, 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('pnpm-lock.yaml')); + $files->delete(base_path('yarn.lock')); + $files->delete(base_path('bun.lock')); + $files->delete(base_path('bun.lockb')); + $files->delete(base_path('deno.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() + { + if (function_exists('Illuminate\Support\php_binary')) { + return \Illuminate\Support\php_binary(); + } + + 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. + * + * @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', + ], + scroll: 6, + ), + ]; + } + + /** + * Interact further with the user if they were prompted for missing arguments. + * + * @return void + */ + protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output) + { + $stack = $input->getArgument('stack'); + } + + /** + * Determine whether the project is already using Pest. + * + * @return bool + */ + protected function isUsingPest() + { + return class_exists(\Pest\TestSuite::class); + } +}
