Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
67 / 67 |
|
100.00% |
10 / 10 |
CRAP | |
100.00% |
1 / 1 |
HydeBuildStaticSiteCommand | |
100.00% |
67 / 67 |
|
100.00% |
10 / 10 |
27 | |
100.00% |
1 / 1 |
handle | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
5 | |||
runPreBuildActions | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
3 | |||
runPostBuildActions | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
9 | |||
printFinishMessage | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
purge | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
getModelPluralName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
runNodeCommand | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
canGenerateSitemap | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
canGenerateFeed | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
canGenerateSearch | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace Hyde\Framework\Commands; |
4 | |
5 | use Exception; |
6 | use Hyde\Framework\Actions\CreatesDefaultDirectories; |
7 | use Hyde\Framework\Concerns\Internal\BuildActionRunner; |
8 | use Hyde\Framework\Concerns\Internal\TransfersMediaAssetsForBuildCommands; |
9 | use Hyde\Framework\Helpers\Features; |
10 | use Hyde\Framework\Hyde; |
11 | use Hyde\Framework\Models\BladePage; |
12 | use Hyde\Framework\Models\DocumentationPage; |
13 | use Hyde\Framework\Models\MarkdownPage; |
14 | use Hyde\Framework\Models\MarkdownPost; |
15 | use Hyde\Framework\Services\CollectionService; |
16 | use Hyde\Framework\Services\DiscoveryService; |
17 | use Hyde\Framework\Services\RssFeedService; |
18 | use Hyde\Framework\Services\SitemapService; |
19 | use Illuminate\Support\Facades\Artisan; |
20 | use Illuminate\Support\Facades\Config; |
21 | use Illuminate\Support\Facades\File; |
22 | use LaravelZero\Framework\Commands\Command; |
23 | |
24 | /** |
25 | * Hyde Command to run the Build Process. |
26 | * |
27 | * @see \Tests\Feature\Commands\BuildStaticSiteCommandTest |
28 | */ |
29 | class HydeBuildStaticSiteCommand extends Command |
30 | { |
31 | use BuildActionRunner; |
32 | use TransfersMediaAssetsForBuildCommands; |
33 | |
34 | /** |
35 | * The signature of the command. |
36 | * |
37 | * @var string |
38 | */ |
39 | protected $signature = 'build |
40 | {--run-dev : Run the NPM dev script after build} |
41 | {--run-prod : Run the NPM prod script after build} |
42 | {--pretty : Deprecated option, use --run-prettier instead} |
43 | {--run-prettier : Format the output using NPM Prettier} |
44 | {--pretty-urls : Should links in output use pretty URLs?} |
45 | {--no-api : Disable API calls, for example, Torchlight}'; |
46 | |
47 | /** |
48 | * The description of the command. |
49 | * |
50 | * @var string |
51 | */ |
52 | protected $description = 'Build the static site'; |
53 | |
54 | /** |
55 | * Execute the console command. |
56 | * |
57 | * @return int |
58 | * |
59 | * @throws Exception |
60 | */ |
61 | public function handle(): int |
62 | { |
63 | $time_start = microtime(true); |
64 | |
65 | $this->title('Building your static site!'); |
66 | |
67 | $this->runPreBuildActions(); |
68 | |
69 | $this->purge(); |
70 | |
71 | $this->transferMediaAssets(); |
72 | |
73 | if (Features::hasBladePages()) { |
74 | $this->runBuildAction(BladePage::class); |
75 | } |
76 | |
77 | if (Features::hasMarkdownPages()) { |
78 | $this->runBuildAction(MarkdownPage::class); |
79 | } |
80 | |
81 | if (Features::hasBlogPosts()) { |
82 | $this->runBuildAction(MarkdownPost::class); |
83 | } |
84 | |
85 | if (Features::hasDocumentationPages()) { |
86 | $this->runBuildAction(DocumentationPage::class); |
87 | } |
88 | |
89 | $this->runPostBuildActions(); |
90 | |
91 | $this->printFinishMessage($time_start); |
92 | |
93 | return 0; |
94 | } |
95 | |
96 | /** @internal */ |
97 | protected function runPreBuildActions(): void |
98 | { |
99 | if ($this->option('no-api')) { |
100 | $this->info('Disabling external API calls'); |
101 | $this->newLine(); |
102 | $config = config('hyde.features'); |
103 | unset($config[array_search('torchlight', $config)]); |
104 | Config::set(['hyde.features' => $config]); |
105 | } |
106 | |
107 | if ($this->option('pretty-urls')) { |
108 | $this->info('Generating site with pretty URLs'); |
109 | $this->newLine(); |
110 | Config::set(['hyde.pretty_urls' => true]); |
111 | } |
112 | } |
113 | |
114 | /** |
115 | * Run any post-build actions. |
116 | * |
117 | * @return void |
118 | */ |
119 | public function runPostBuildActions(): void |
120 | { |
121 | if ($this->option('run-prettier') || $this->option('pretty')) { |
122 | if ($this->option('pretty')) { |
123 | $this->warn('<error>Warning:</> The --pretty option is deprecated, use --run-prettier instead'); |
124 | } |
125 | $this->runNodeCommand( |
126 | 'npx prettier '.Hyde::pathToRelative(Hyde::getSiteOutputPath()).'/ --write --bracket-same-line', |
127 | 'Prettifying code!', |
128 | 'prettify code' |
129 | ); |
130 | } |
131 | |
132 | if ($this->option('run-dev')) { |
133 | $this->runNodeCommand('npm run dev', 'Building frontend assets for development!'); |
134 | } |
135 | |
136 | if ($this->option('run-prod')) { |
137 | $this->runNodeCommand('npm run prod', 'Building frontend assets for production!'); |
138 | } |
139 | |
140 | if ($this->canGenerateSitemap()) { |
141 | Artisan::call('build:sitemap', outputBuffer: $this->output); |
142 | } |
143 | |
144 | if ($this->canGenerateFeed()) { |
145 | Artisan::call('build:rss', outputBuffer: $this->output); |
146 | } |
147 | |
148 | if ($this->canGenerateSearch()) { |
149 | Artisan::call('build:search', outputBuffer: $this->output); |
150 | } |
151 | } |
152 | |
153 | /** @internal */ |
154 | protected function printFinishMessage(float $time_start): void |
155 | { |
156 | $time_end = microtime(true); |
157 | $execution_time = ($time_end - $time_start); |
158 | $this->info('All done! Finished in '.number_format( |
159 | $execution_time, |
160 | 2 |
161 | ).' seconds. ('.number_format(($execution_time * 1000), 2).'ms)'); |
162 | |
163 | $this->info('Congratulations! 🎉 Your static site has been built!'); |
164 | $this->line( |
165 | 'Your new homepage is stored here -> '. |
166 | DiscoveryService::createClickableFilepath(Hyde::getSiteOutputPath('index.html')) |
167 | ); |
168 | } |
169 | |
170 | /** |
171 | * Clear the entire output directory before running the build. |
172 | * |
173 | * @return void |
174 | */ |
175 | public function purge(): void |
176 | { |
177 | $this->warn('Removing all files from build directory.'); |
178 | |
179 | File::deleteDirectory(Hyde::getSiteOutputPath()); |
180 | mkdir(Hyde::getSiteOutputPath()); |
181 | |
182 | $this->line('<fg=gray> > Directory purged'); |
183 | |
184 | $this->line(' > Recreating directories'); |
185 | (new CreatesDefaultDirectories)->__invoke(); |
186 | |
187 | $this->line('</>'); |
188 | } |
189 | |
190 | /** @internal */ |
191 | protected function getModelPluralName(string $model): string |
192 | { |
193 | return preg_replace('/([a-z])([A-Z])/', '$1 $2', class_basename($model)).'s'; |
194 | } |
195 | |
196 | /* @internal */ |
197 | private function runNodeCommand(string $command, string $message, ?string $actionMessage = null): void |
198 | { |
199 | $this->info($message.' This may take a second.'); |
200 | |
201 | if (app()->environment() === 'testing') { |
202 | $command = 'echo '.$command; |
203 | } |
204 | $output = shell_exec($command); |
205 | |
206 | $this->line( |
207 | $output ?? '<fg=red>Could not '.($actionMessage ?? 'run script').'! Is NPM installed?</>' |
208 | ); |
209 | } |
210 | |
211 | protected function canGenerateSitemap(): bool |
212 | { |
213 | return SitemapService::canGenerateSitemap(); |
214 | } |
215 | |
216 | protected function canGenerateFeed(): bool |
217 | { |
218 | return RssFeedService::canGenerateFeed() |
219 | && count(CollectionService::getMarkdownPostList()) > 0; |
220 | } |
221 | |
222 | protected function canGenerateSearch(): bool |
223 | { |
224 | return Features::hasDocumentationSearch() |
225 | && count(CollectionService::getDocumentationPageList()) > 0; |
226 | } |
227 | } |