|
0
|
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 }
|