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 }