1 | <?php |
||||
2 | |||||
3 | /** |
||||
4 | * This file is part of Cecil. |
||||
5 | * |
||||
6 | * (c) Arnaud Ligny <[email protected]> |
||||
7 | * |
||||
8 | * For the full copyright and license information, please view the LICENSE |
||||
9 | * file that was distributed with this source code. |
||||
10 | */ |
||||
11 | |||||
12 | declare(strict_types=1); |
||||
13 | |||||
14 | namespace Cecil\Command; |
||||
15 | |||||
16 | use Cecil\Util; |
||||
17 | use Symfony\Component\Console\Helper\Table; |
||||
18 | use Symfony\Component\Console\Input\InputArgument; |
||||
19 | use Symfony\Component\Console\Input\InputInterface; |
||||
20 | use Symfony\Component\Console\Input\InputOption; |
||||
21 | use Symfony\Component\Console\Output\OutputInterface; |
||||
22 | |||||
23 | /** |
||||
24 | * Build command. |
||||
25 | * |
||||
26 | * This command generates the website in the output directory. |
||||
27 | * It can include drafts, optimize generated files, and perform a dry run. |
||||
28 | * It also allows building a specific page or a subset of pages, clearing the cache, and showing build metrics. |
||||
29 | */ |
||||
30 | class Build extends AbstractCommand |
||||
31 | { |
||||
32 | /** |
||||
33 | * {@inheritdoc} |
||||
34 | */ |
||||
35 | protected function configure() |
||||
36 | { |
||||
37 | $this |
||||
38 | ->setName('build') |
||||
39 | ->setDescription('Builds the website') |
||||
40 | ->setDefinition([ |
||||
41 | new InputArgument('path', InputArgument::OPTIONAL, 'Use the given path as working directory'), |
||||
42 | new InputOption('drafts', 'd', InputOption::VALUE_NONE, 'Include drafts'), |
||||
43 | new InputOption('baseurl', 'u', InputOption::VALUE_REQUIRED, 'Set the base URL'), |
||||
44 | new InputOption('output', 'o', InputOption::VALUE_REQUIRED, 'Set the output directory'), |
||||
45 | new InputOption('optimize', null, InputOption::VALUE_NEGATABLE, 'Enable (or disable --no-optimize) optimization of generated files'), |
||||
46 | new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Build without saving'), |
||||
47 | new InputOption('config', 'c', InputOption::VALUE_REQUIRED, 'Set the path to extra config files (comma-separated)'), |
||||
48 | new InputOption('clear-cache', null, InputOption::VALUE_OPTIONAL, 'Clear cache before build (optional cache key as regular expression)', false), |
||||
49 | new InputOption('page', 'p', InputOption::VALUE_REQUIRED, 'Build a specific page'), |
||||
50 | new InputOption('render-subset', null, InputOption::VALUE_REQUIRED, 'Render a subset of pages'), |
||||
51 | new InputOption('show-pages', null, InputOption::VALUE_NONE, 'Show list of built pages in a table'), |
||||
52 | new InputOption('metrics', 'm', InputOption::VALUE_NONE, 'Show build metrics (duration and memory) of each step'), |
||||
53 | ]) |
||||
54 | ->setHelp( |
||||
55 | <<<'EOF' |
||||
56 | The <info>%command.name%</> command generates the website in the <comment>output</comment> directory. |
||||
57 | |||||
58 | <info>%command.full_name%</> |
||||
59 | <info>%command.full_name% path/to/the/working/directory</> |
||||
60 | <info>%command.full_name% --baseurl=https://example.com/</> |
||||
61 | <info>%command.full_name% --output=_site</> |
||||
62 | |||||
63 | To build the website with <comment>optimization</comment> of generated files, you can use the <info>--optimize</info> option. |
||||
64 | This is useful to reduce the size of the generated files and <comment>improve performance</comment>: |
||||
65 | |||||
66 | <info>%command.full_name% --optimize</> |
||||
67 | <info>%command.full_name% --no-optimize</> |
||||
68 | |||||
69 | To build the website <comment>without overwriting files in the output</comment> directory, you can use the <info>--dry-run</info> option. |
||||
70 | This is useful to check what would be built without actually writing files: |
||||
71 | |||||
72 | <info>%command.full_name% --dry-run</> |
||||
73 | |||||
74 | To build the website with a specific subset of rendered pages, you can use the <info>--render-subset</info> option. |
||||
75 | This is useful to <comment>build only a part of the website</comment>, for example, only "hot" pages or a specific section: |
||||
76 | |||||
77 | <info>%command.full_name% --render-subset=subset</> |
||||
78 | |||||
79 | To show build steps <comment>metrics</comment>, run: |
||||
80 | |||||
81 | <info>%command.full_name% --metrics</> |
||||
82 | EOF |
||||
83 | ); |
||||
84 | } |
||||
85 | |||||
86 | /** |
||||
87 | * {@inheritdoc} |
||||
88 | */ |
||||
89 | protected function execute(InputInterface $input, OutputInterface $output) |
||||
90 | { |
||||
91 | $config = []; |
||||
92 | $options = []; |
||||
93 | $messageOpt = ''; |
||||
94 | |||||
95 | if ($input->getOption('baseurl')) { |
||||
96 | $config['baseurl'] = $input->getOption('baseurl'); |
||||
97 | } |
||||
98 | if ($input->getOption('output')) { |
||||
99 | $config['output']['dir'] = $input->getOption('output'); |
||||
100 | Util\File::getFS()->dumpFile(Util::joinFile($this->getPath(), self::TMP_DIR, 'output'), (string) $input->getOption('output')); |
||||
101 | } |
||||
102 | if ($input->getOption('optimize') === true) { |
||||
103 | $config['optimize']['enabled'] = true; |
||||
104 | } |
||||
105 | if ($input->getOption('optimize') === false) { |
||||
106 | $config['optimize']['enabled'] = false; |
||||
107 | } |
||||
108 | if ($input->getOption('clear-cache') === null) { |
||||
109 | $config['cache']['enabled'] = false; |
||||
110 | } |
||||
111 | |||||
112 | $builder = $this->getBuilder($config); |
||||
113 | |||||
114 | if ($input->getOption('drafts')) { |
||||
115 | $options['drafts'] = true; |
||||
116 | $messageOpt .= ' with drafts'; |
||||
117 | } |
||||
118 | if ($input->getOption('dry-run')) { |
||||
119 | $options['dry-run'] = true; |
||||
120 | $messageOpt .= ' (dry-run)'; |
||||
121 | } |
||||
122 | if ($input->getOption('page')) { |
||||
123 | $options['page'] = $input->getOption('page'); |
||||
124 | } |
||||
125 | if ($input->getOption('render-subset')) { |
||||
126 | $options['render-subset'] = (string) $input->getOption('render-subset'); |
||||
127 | } |
||||
128 | if ($input->getOption('clear-cache')) { |
||||
129 | if (0 < $removedFiles = (new \Cecil\Assets\Cache($this->getBuilder()))->clearByPattern((string) $input->getOption('clear-cache'))) { |
||||
130 | $output->writeln(\sprintf('<info>%s cache files removed by regular expression "%s"</info>', $removedFiles, $input->getOption('clear-cache'))); |
||||
131 | } |
||||
132 | } |
||||
133 | |||||
134 | $output->writeln(\sprintf('Building website%s...', $messageOpt)); |
||||
135 | $output->writeln(\sprintf('<comment>Path: %s</comment>', $this->getPath()), OutputInterface::VERBOSITY_VERY_VERBOSE); |
||||
136 | if (!empty($this->getConfigFiles())) { |
||||
137 | $output->writeln(\sprintf('<comment>Config: %s</comment>', implode(', ', $this->getConfigFiles())), OutputInterface::VERBOSITY_VERY_VERBOSE); |
||||
138 | } |
||||
139 | $output->writeln(\sprintf('<comment>Output: %s</comment>', $this->getBuilder()->getConfig()->getOutputPath()), OutputInterface::VERBOSITY_VERY_VERBOSE); |
||||
140 | if ($builder->getConfig()->isEnabled('cache') !== false) { |
||||
141 | $output->writeln(\sprintf('<comment>Cache: %s</comment>', $builder->getConfig()->getCachePath()), OutputInterface::VERBOSITY_VERY_VERBOSE); |
||||
142 | } |
||||
143 | |||||
144 | // build |
||||
145 | $builder->build($options); |
||||
146 | $output->writeln('Done 🎉'); |
||||
147 | |||||
148 | // show build steps metrics |
||||
149 | if ($input->getOption('metrics')) { |
||||
150 | $table = new Table($output); |
||||
151 | $table |
||||
152 | ->setHeaderTitle('Build steps metrics') |
||||
153 | ->setHeaders(['Step', 'Duration', 'Memory']) |
||||
154 | ->setRows($builder->getMetrics()['steps']) |
||||
155 | ; |
||||
156 | $table->setStyle('box')->render(); |
||||
157 | } |
||||
158 | |||||
159 | // show built pages as table |
||||
160 | if ($input->getOption('show-pages')) { |
||||
161 | $pagesAsArray = []; |
||||
162 | foreach ( |
||||
163 | $this->getBuilder()->getPages()->filter(function (\Cecil\Collection\Page\Page $page) { |
||||
164 | return $page->getVariable('published'); |
||||
165 | })->usort(function (\Cecil\Collection\Page\Page $pageA, \Cecil\Collection\Page\Page $pageB) { |
||||
166 | return strnatcmp($pageA['language'], $pageB['language']); |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() It seems like
$pageB['language'] can also be of type null ; however, parameter $string2 of strnatcmp() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
167 | }) as $page |
||||
168 | ) { |
||||
169 | /** @var \Cecil\Collection\Page\Page $page */ |
||||
170 | $pagesAsArray[] = [ |
||||
171 | $page->getId(), |
||||
172 | $page->getVariable('language'), |
||||
173 | \sprintf("%s %s", $page->getType(), $page->getType() !== \Cecil\Collection\Page\Type::PAGE->value ? "(" . \count($page->getPages() ?: []) . ")" : ''), |
||||
174 | $page->getSection(), |
||||
175 | $page->isVirtual() ? 'true' : 'false', |
||||
176 | ]; |
||||
177 | } |
||||
178 | $table = new Table($output); |
||||
179 | $table |
||||
180 | ->setHeaderTitle(\sprintf("Built pages (%s)", \count($pagesAsArray))) |
||||
181 | ->setHeaders(['ID', 'Lang', 'Type', 'Section', 'Virtual']) |
||||
182 | ->setRows($pagesAsArray) |
||||
183 | ; |
||||
184 | $table->setStyle('box')->render(); |
||||
185 | } |
||||
186 | |||||
187 | return 0; |
||||
188 | } |
||||
189 | } |
||||
190 |