diff --git a/src/Commands/StaticSiteGenerate.php b/src/Commands/StaticSiteGenerate.php index b452ca3..639dbb5 100644 --- a/src/Commands/StaticSiteGenerate.php +++ b/src/Commands/StaticSiteGenerate.php @@ -9,6 +9,8 @@ use Statamic\StaticSite\Generator; use Wilderborn\Partyline\Facade as Partyline; +use function Laravel\Prompts\confirm; + class StaticSiteGenerate extends Command { use RunsInPlease; @@ -61,19 +63,19 @@ public function handle() Partyline::bind($this); if (config('statamic.editions.pro') && ! config('statamic.system.license_key')) { - $this->error('Statamic Pro is enabled but no site license was found.'); - $this->warn('Please set a valid Statamic License Key in your .env file.'); + $this->components->error('Statamic Pro is enabled but no site license was found.'); + $this->components->warn('Please set a valid Statamic License Key in your .env file.'); $confirmationText = 'By continuing you agree that this build is for testing purposes only. Do you wish to continue?'; - if (! $this->option('no-interaction') && ! $this->confirm($confirmationText)) { - $this->line('Static site generation canceled.'); + if (! $this->option('no-interaction') && ! confirm($confirmationText)) { + $this->components->error('Static site generation canceled.'); return 0; } } if (! $workers = $this->option('workers')) { - $this->comment('You may be able to speed up site generation significantly by installing spatie/fork and using multiple workers (requires PHP 8+).'); + $this->components->info('You may be able to speed up site generation significantly by installing spatie/fork and using multiple workers.'); } try { @@ -82,8 +84,8 @@ public function handle() ->disableClear($this->option('disable-clear') ?? false) ->generate($this->argument('urls') ?: '*'); } catch (GenerationFailedException $e) { - $this->line($e->getConsoleMessage()); - $this->error('Static site generation failed.'); + $this->components->error($e->getConsoleMessage()); + $this->components->error('Static site generation failed.'); return 1; } diff --git a/src/Generator.php b/src/Generator.php index a4d1593..4431e6c 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -27,6 +27,8 @@ use Statamic\Support\Str; use Wilderborn\Partyline\Facade as Partyline; +use function Laravel\Prompts\spin; + class Generator { protected $app; @@ -154,10 +156,10 @@ public function createSymlinks() $dest = $this->config['destination'].'/'.$dest; if ($this->files->exists($dest)) { - Partyline::line("Symlink not created. $dest already exists."); + Partyline::outputComponents()->twoColumnDetail("$source symlinked to $dest", 'SKIPPED. SYMLINK ALREADY EXISTS'); } else { $this->files->link($source, $dest); - Partyline::line("[✔] $source symlinked to $dest"); + Partyline::outputComponents()->twoColumnDetail("$source symlinked to $dest", 'SUCCESS'); } } @@ -175,7 +177,7 @@ public function copyFiles() $this->files->copyDirectory($source, $dest); } - Partyline::line("[✔] $source copied to $dest"); + Partyline::outputComponents()->twoColumnDetail("$source copied to $dest", 'SUCCESS'); } return $this; @@ -195,7 +197,7 @@ protected function createContentFiles($urls = '*') $pages = $pages->shuffle(); } - Partyline::line("Generating {$pages->count()} content files..."); + Partyline::outputComponents()->info("Generating {$pages->count()} content files..."); $closures = $this->makeContentGenerationClosures($pages, $request); @@ -236,11 +238,12 @@ protected function gatherContent($urls = '*') ->reject(fn ($page) => $this->shouldRejectPage($page, true)); } - Partyline::line('Gathering content to be generated...'); - - $pages = $this->gatherAllPages(); + $pages = spin( + fn () => $this->gatherAllPages(), + 'Gathering content to be generated...', + ); - Partyline::line("\x1B[1A\x1B[2K[✔] Gathered content to be generated"); + Partyline::outputComponents()->info('Gathered content to be generated'); return $pages; } @@ -282,7 +285,11 @@ protected function makeContentGenerationClosures($pages, $request) $request->setPage($page); - Partyline::line("\x1B[1A\x1B[2KGenerating ".$page->url()); + // Only write the "Generating" line when using a single worker. + // Otherwise, the output will be messed up. + if ($this->workers === 1) { + $this->writeGeneratingLine($page->url()); + } try { $generated = $page->generate($request); @@ -291,6 +298,12 @@ protected function makeContentGenerationClosures($pages, $request) return $e->consoleMessage(); } + if ($this->workers === 1) { + $this->clearCurrentLine(); + } + + Partyline::outputComponents()->twoColumnDetail($page->url(), 'FAILED'); + $errors[] = $e->consoleMessage(); continue; @@ -306,6 +319,12 @@ protected function makeContentGenerationClosures($pages, $request) $warnings[] = $generated->consoleMessage(); } + if ($this->workers === 1) { + $this->clearCurrentLine(); + } + + Partyline::outputComponents()->twoColumnDetail($page->url(), 'SUCCESS'); + Blink::flush(); } @@ -320,24 +339,25 @@ protected function outputTasksResults() $successCount = $results['count'] - $results['errors']->count(); - Partyline::line("\x1B[1A\x1B[2K[✔] Generated {$successCount} content files"); + Partyline::newLine(); + Partyline::outputComponents()->success("Generated {$successCount} content files"); - $results['warnings']->merge($results['errors'])->each(fn ($error) => Partyline::line($error)); + $results['warnings']->each(fn ($warning) => Partyline::outputComponents()->warn($warning)); + $results['errors']->each(fn ($error) => Partyline::outputComponents()->error($error)); } protected function outputSummary() { - Partyline::info(''); - Partyline::info('Static site generated into '.$this->config['destination']); + Partyline::outputComponents()->success('Static site generated into '.$this->config['destination']); $total = $this->taskResults['count']; if ($errors = count($this->taskResults['errors'])) { - Partyline::warn("[!] {$errors}/{$total} pages not generated"); + Partyline::outputComponents()->warn("{$errors}/{$total} pages not generated"); } if ($warnings = count($this->taskResults['warnings'])) { - Partyline::warn("[!] {$warnings}/{$total} pages generated with warnings"); + Partyline::outputComponents()->warn("{$warnings}/{$total} pages generated with warnings"); } } @@ -438,7 +458,7 @@ protected function checkConcurrencySupport() return; } - throw new \RuntimeException('To use multiple workers, you must install PHP 8 and spatie/fork.'); + throw new \RuntimeException('To use multiple workers, you must install spatie/fork.'); } protected function shouldFail($item) @@ -500,4 +520,26 @@ protected function getToStringFormat(): string|\Closure|null return Arr::get($factory->invoke($date)->getSettings(), 'toStringFormat'); } + + /** + * Outputs a "Generating" line to the terminal. Very similar output-wise to Laravel's + * ->twoColumnDetail() method, but it allows for replacing the line later, for + * success/error statuses. + */ + private function writeGeneratingLine(string $url): void + { + $dotsLen = max(1, 150 - strlen($url) - strlen('GENERATING') - 6); + + $line = sprintf(" %s \033[90m%s\033[0m \033[34;1mGENERATING\033[0m", + $url, + str_repeat('.', $dotsLen) + ); + + fwrite(STDERR, $line); + } + + private function clearCurrentLine(): void + { + fwrite(STDERR, "\r\x1B[2K"); + } }