Mercurial > packages > auth
comparison src/Console/InstallCommand.php @ 0:90e38de8f2ba
Initial Commit
| author | luka |
|---|---|
| date | Wed, 13 Aug 2025 22:17:20 -0400 |
| parents | |
| children | ac199a7a8931 |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:90e38de8f2ba |
|---|---|
| 1 <?php | |
| 2 | |
| 3 namespace Wizard\Auth\Console; | |
| 4 | |
| 5 use Illuminate\Console\Command; | |
| 6 use Illuminate\Contracts\Console\PromptsForMissingInput; | |
| 7 use Illuminate\Filesystem\Filesystem; | |
| 8 use Illuminate\Support\Arr; | |
| 9 use Illuminate\Support\Str; | |
| 10 use RuntimeException; | |
| 11 use Symfony\Component\Console\Attribute\AsCommand; | |
| 12 use Symfony\Component\Console\Input\InputInterface; | |
| 13 use Symfony\Component\Console\Output\OutputInterface; | |
| 14 use Symfony\Component\Finder\Finder; | |
| 15 use Symfony\Component\Process\PhpExecutableFinder; | |
| 16 use Symfony\Component\Process\Process; | |
| 17 | |
| 18 use function Laravel\Prompts\confirm; | |
| 19 use function Laravel\Prompts\multiselect; | |
| 20 use function Laravel\Prompts\select; | |
| 21 | |
| 22 #[AsCommand(name: 'breeze:install')] | |
| 23 class InstallCommand extends Command implements PromptsForMissingInput | |
| 24 { | |
| 25 use InstallsBladeStack; | |
| 26 | |
| 27 /** | |
| 28 * The name and signature of the console command. | |
| 29 * | |
| 30 * @var string | |
| 31 */ | |
| 32 protected $signature = 'breeze:install {stack : The development stack that should be installed (blade)}'; | |
| 33 | |
| 34 /** | |
| 35 * The console command description. | |
| 36 * | |
| 37 * @var string | |
| 38 */ | |
| 39 protected $description = 'Install the Breeze controllers and resources'; | |
| 40 | |
| 41 /** | |
| 42 * Execute the console command. | |
| 43 * | |
| 44 * @return int|null | |
| 45 */ | |
| 46 public function handle() | |
| 47 { | |
| 48 return $this->installBladeStack(); | |
| 49 } | |
| 50 | |
| 51 /** | |
| 52 * Install Breeze's tests. | |
| 53 * | |
| 54 * @return bool | |
| 55 */ | |
| 56 protected function installTests() | |
| 57 { | |
| 58 (new Filesystem)->ensureDirectoryExists(base_path('tests/Feature')); | |
| 59 | |
| 60 $stubStack = match ($this->argument('stack')) { | |
| 61 default => 'default', | |
| 62 }; | |
| 63 | |
| 64 (new Filesystem)->copyDirectory(__DIR__.'/../../stubs/'.$stubStack.'/tests/Feature', base_path('tests/Feature')); | |
| 65 | |
| 66 return true; | |
| 67 } | |
| 68 | |
| 69 /** | |
| 70 * Install the given middleware names into the application. | |
| 71 * | |
| 72 * @param array|string $name | |
| 73 * @param string $group | |
| 74 * @param string $modifier | |
| 75 * @return void | |
| 76 */ | |
| 77 protected function installMiddleware($names, $group = 'web', $modifier = 'append') | |
| 78 { | |
| 79 $bootstrapApp = file_get_contents(base_path('bootstrap/app.php')); | |
| 80 | |
| 81 $names = collect(Arr::wrap($names)) | |
| 82 ->filter(fn ($name) => ! Str::contains($bootstrapApp, $name)) | |
| 83 ->whenNotEmpty(function ($names) use ($bootstrapApp, $group, $modifier) { | |
| 84 $names = $names->map(fn ($name) => "$name")->implode(','.PHP_EOL.' '); | |
| 85 | |
| 86 $stubs = [ | |
| 87 '->withMiddleware(function (Middleware $middleware) {', | |
| 88 '->withMiddleware(function (Middleware $middleware): void {', | |
| 89 ]; | |
| 90 | |
| 91 $bootstrapApp = str_replace( | |
| 92 $stubs, | |
| 93 collect($stubs)->transform(fn ($stub) => $stub | |
| 94 .PHP_EOL." \$middleware->$group($modifier: [" | |
| 95 .PHP_EOL." $names," | |
| 96 .PHP_EOL.' ]);' | |
| 97 .PHP_EOL | |
| 98 )->all(), | |
| 99 $bootstrapApp, | |
| 100 ); | |
| 101 | |
| 102 file_put_contents(base_path('bootstrap/app.php'), $bootstrapApp); | |
| 103 }); | |
| 104 } | |
| 105 | |
| 106 /** | |
| 107 * Install the given middleware aliases into the application. | |
| 108 * | |
| 109 * @param array $aliases | |
| 110 * @return void | |
| 111 */ | |
| 112 protected function installMiddlewareAliases($aliases) | |
| 113 { | |
| 114 $bootstrapApp = file_get_contents(base_path('bootstrap/app.php')); | |
| 115 | |
| 116 $aliases = collect($aliases) | |
| 117 ->filter(fn ($alias) => ! Str::contains($bootstrapApp, $alias)) | |
| 118 ->whenNotEmpty(function ($aliases) use ($bootstrapApp) { | |
| 119 $aliases = $aliases->map(fn ($name, $alias) => "'$alias' => $name")->implode(','.PHP_EOL.' '); | |
| 120 | |
| 121 $stubs = [ | |
| 122 '->withMiddleware(function (Middleware $middleware) {', | |
| 123 '->withMiddleware(function (Middleware $middleware): void {', | |
| 124 ]; | |
| 125 | |
| 126 $bootstrapApp = str_replace( | |
| 127 $stubs, | |
| 128 collect($stubs)->transform(fn ($stub) => $stub | |
| 129 .PHP_EOL.' $middleware->alias([' | |
| 130 .PHP_EOL." $aliases," | |
| 131 .PHP_EOL.' ]);' | |
| 132 .PHP_EOL | |
| 133 )->all(), | |
| 134 $bootstrapApp, | |
| 135 ); | |
| 136 | |
| 137 file_put_contents(base_path('bootstrap/app.php'), $bootstrapApp); | |
| 138 }); | |
| 139 } | |
| 140 | |
| 141 /** | |
| 142 * Determine if the given Composer package is installed. | |
| 143 * | |
| 144 * @param string $package | |
| 145 * @return bool | |
| 146 */ | |
| 147 protected function hasComposerPackage($package) | |
| 148 { | |
| 149 $packages = json_decode(file_get_contents(base_path('composer.json')), true); | |
| 150 | |
| 151 return array_key_exists($package, $packages['require'] ?? []) | |
| 152 || array_key_exists($package, $packages['require-dev'] ?? []); | |
| 153 } | |
| 154 | |
| 155 /** | |
| 156 * Installs the given Composer Packages into the application. | |
| 157 * | |
| 158 * @param bool $asDev | |
| 159 * @return bool | |
| 160 */ | |
| 161 protected function requireComposerPackages(array $packages, $asDev = false) | |
| 162 { | |
| 163 $composer = $this->option('composer'); | |
| 164 | |
| 165 if ($composer !== 'global') { | |
| 166 $command = ['php', $composer, 'require']; | |
| 167 } | |
| 168 | |
| 169 $command = array_merge( | |
| 170 $command ?? ['composer', 'require'], | |
| 171 $packages, | |
| 172 $asDev ? ['--dev'] : [], | |
| 173 ); | |
| 174 | |
| 175 return (new Process($command, base_path(), ['COMPOSER_MEMORY_LIMIT' => '-1'])) | |
| 176 ->setTimeout(null) | |
| 177 ->run(function ($type, $output) { | |
| 178 $this->output->write($output); | |
| 179 }) === 0; | |
| 180 } | |
| 181 | |
| 182 /** | |
| 183 * Removes the given Composer Packages from the application. | |
| 184 * | |
| 185 * @param bool $asDev | |
| 186 * @return bool | |
| 187 */ | |
| 188 protected function removeComposerPackages(array $packages, $asDev = false) | |
| 189 { | |
| 190 $composer = $this->option('composer'); | |
| 191 | |
| 192 if ($composer !== 'global') { | |
| 193 $command = ['php', $composer, 'remove']; | |
| 194 } | |
| 195 | |
| 196 $command = array_merge( | |
| 197 $command ?? ['composer', 'remove'], | |
| 198 $packages, | |
| 199 $asDev ? ['--dev'] : [], | |
| 200 ); | |
| 201 | |
| 202 return (new Process($command, base_path(), ['COMPOSER_MEMORY_LIMIT' => '-1'])) | |
| 203 ->setTimeout(null) | |
| 204 ->run(function ($type, $output) { | |
| 205 $this->output->write($output); | |
| 206 }) === 0; | |
| 207 } | |
| 208 | |
| 209 /** | |
| 210 * Update the dependencies in the "package.json" file. | |
| 211 * | |
| 212 * @param bool $dev | |
| 213 * @return void | |
| 214 */ | |
| 215 protected static function updateNodePackages(callable $callback, $dev = true) | |
| 216 { | |
| 217 if (! file_exists(base_path('package.json'))) { | |
| 218 return; | |
| 219 } | |
| 220 | |
| 221 $configurationKey = $dev ? 'devDependencies' : 'dependencies'; | |
| 222 | |
| 223 $packages = json_decode(file_get_contents(base_path('package.json')), true); | |
| 224 | |
| 225 $packages[$configurationKey] = $callback( | |
| 226 array_key_exists($configurationKey, $packages) ? $packages[$configurationKey] : [], | |
| 227 $configurationKey | |
| 228 ); | |
| 229 | |
| 230 ksort($packages[$configurationKey]); | |
| 231 | |
| 232 file_put_contents( | |
| 233 base_path('package.json'), | |
| 234 json_encode($packages, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT).PHP_EOL | |
| 235 ); | |
| 236 } | |
| 237 | |
| 238 /** | |
| 239 * Update the scripts in the "package.json" file. | |
| 240 * | |
| 241 * @return void | |
| 242 */ | |
| 243 protected static function updateNodeScripts(callable $callback) | |
| 244 { | |
| 245 if (! file_exists(base_path('package.json'))) { | |
| 246 return; | |
| 247 } | |
| 248 | |
| 249 $content = json_decode(file_get_contents(base_path('package.json')), true); | |
| 250 | |
| 251 $content['scripts'] = $callback( | |
| 252 array_key_exists('scripts', $content) ? $content['scripts'] : [] | |
| 253 ); | |
| 254 | |
| 255 file_put_contents( | |
| 256 base_path('package.json'), | |
| 257 json_encode($content, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT).PHP_EOL | |
| 258 ); | |
| 259 } | |
| 260 | |
| 261 /** | |
| 262 * Delete the "node_modules" directory and remove the associated lock files. | |
| 263 * | |
| 264 * @return void | |
| 265 */ | |
| 266 protected static function flushNodeModules() | |
| 267 { | |
| 268 tap(new Filesystem, function ($files) { | |
| 269 $files->deleteDirectory(base_path('node_modules')); | |
| 270 | |
| 271 $files->delete(base_path('pnpm-lock.yaml')); | |
| 272 $files->delete(base_path('yarn.lock')); | |
| 273 $files->delete(base_path('bun.lock')); | |
| 274 $files->delete(base_path('bun.lockb')); | |
| 275 $files->delete(base_path('deno.lock')); | |
| 276 $files->delete(base_path('package-lock.json')); | |
| 277 }); | |
| 278 } | |
| 279 | |
| 280 /** | |
| 281 * Replace a given string within a given file. | |
| 282 * | |
| 283 * @param string $search | |
| 284 * @param string $replace | |
| 285 * @param string $path | |
| 286 * @return void | |
| 287 */ | |
| 288 protected function replaceInFile($search, $replace, $path) | |
| 289 { | |
| 290 file_put_contents($path, str_replace($search, $replace, file_get_contents($path))); | |
| 291 } | |
| 292 | |
| 293 /** | |
| 294 * Get the path to the appropriate PHP binary. | |
| 295 * | |
| 296 * @return string | |
| 297 */ | |
| 298 protected function phpBinary() | |
| 299 { | |
| 300 if (function_exists('Illuminate\Support\php_binary')) { | |
| 301 return \Illuminate\Support\php_binary(); | |
| 302 } | |
| 303 | |
| 304 return (new PhpExecutableFinder)->find(false) ?: 'php'; | |
| 305 } | |
| 306 | |
| 307 /** | |
| 308 * Run the given commands. | |
| 309 * | |
| 310 * @param array $commands | |
| 311 * @return void | |
| 312 */ | |
| 313 protected function runCommands($commands) | |
| 314 { | |
| 315 $process = Process::fromShellCommandline(implode(' && ', $commands), null, null, null, null); | |
| 316 | |
| 317 if ('\\' !== DIRECTORY_SEPARATOR && file_exists('/dev/tty') && is_readable('/dev/tty')) { | |
| 318 try { | |
| 319 $process->setTty(true); | |
| 320 } catch (RuntimeException $e) { | |
| 321 $this->output->writeln(' <bg=yellow;fg=black> WARN </> '.$e->getMessage().PHP_EOL); | |
| 322 } | |
| 323 } | |
| 324 | |
| 325 $process->run(function ($type, $line) { | |
| 326 $this->output->write(' '.$line); | |
| 327 }); | |
| 328 } | |
| 329 | |
| 330 /** | |
| 331 * Remove Tailwind dark classes from the given files. | |
| 332 * | |
| 333 * @return void | |
| 334 */ | |
| 335 protected function removeDarkClasses(Finder $finder) | |
| 336 { | |
| 337 foreach ($finder as $file) { | |
| 338 file_put_contents($file->getPathname(), preg_replace('/\sdark:[^\s"\']+/', '', $file->getContents())); | |
| 339 } | |
| 340 } | |
| 341 | |
| 342 /** | |
| 343 * Prompt for missing input arguments using the returned questions. | |
| 344 * | |
| 345 * @return array | |
| 346 */ | |
| 347 protected function promptForMissingArgumentsUsing() | |
| 348 { | |
| 349 return [ | |
| 350 'stack' => fn () => select( | |
| 351 label: 'Which Breeze stack would you like to install?', | |
| 352 options: [ | |
| 353 'blade' => 'Blade', | |
| 354 ], | |
| 355 scroll: 6, | |
| 356 ), | |
| 357 ]; | |
| 358 } | |
| 359 | |
| 360 /** | |
| 361 * Interact further with the user if they were prompted for missing arguments. | |
| 362 * | |
| 363 * @return void | |
| 364 */ | |
| 365 protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output) | |
| 366 { | |
| 367 $stack = $input->getArgument('stack'); | |
| 368 } | |
| 369 | |
| 370 /** | |
| 371 * Determine whether the project is already using Pest. | |
| 372 * | |
| 373 * @return bool | |
| 374 */ | |
| 375 protected function isUsingPest() | |
| 376 { | |
| 377 return class_exists(\Pest\TestSuite::class); | |
| 378 } | |
| 379 } |
