Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
19 / 19
CRAP
100.00% covered (success)
100.00%
1 / 1
BuildTaskService
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
19 / 19
26
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 setOutput
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRegisteredTasks
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 runPreBuildTasks
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 runPostBuildTasks
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 registerTask
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 registerTaskInService
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 registerIf
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 registerTasks
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 findTasksInConfig
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 findTasksInAppDirectory
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 pathToClassName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 makeTaskIdentifier
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 registerFrameworkTasks
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 canCleanSiteDirectory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 canGenerateManifest
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 canGenerateSitemap
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 canGenerateFeed
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 canGenerateSearch
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Hyde\Framework\Services;
6
7use Hyde\Facades\Config;
8use Hyde\Facades\Features;
9use Hyde\Facades\Filesystem;
10use Hyde\Framework\Features\BuildTasks\BuildTask;
11use Hyde\Framework\Features\BuildTasks\PreBuildTask;
12use Hyde\Framework\Features\BuildTasks\PostBuildTask;
13use Hyde\Framework\Actions\PreBuildTasks\CleanSiteDirectory;
14use Hyde\Framework\Actions\PostBuildTasks\GenerateSearch;
15use Hyde\Framework\Actions\PostBuildTasks\GenerateRssFeed;
16use Hyde\Framework\Actions\PostBuildTasks\GenerateSitemap;
17use Hyde\Framework\Actions\PostBuildTasks\GenerateBuildManifest;
18use Illuminate\Console\OutputStyle;
19use Illuminate\Support\Str;
20
21use function array_map;
22use function array_values;
23use function class_basename;
24use function is_string;
25use function str_replace;
26
27/**
28 * This service manages the build tasks that are called before and after the site is compiled using the build command.
29 *
30 * The class is registered as a singleton in the Laravel service container and is run by the build command.
31 * Build Tasks can be registered programmatically, through the config, and through autodiscovery.
32 * The service determines when to run a task depending on which class it extends.
33 */
34class BuildTaskService
35{
36    /** @var array<string, \Hyde\Framework\Features\BuildTasks\BuildTask> */
37    protected array $buildTasks = [];
38
39    protected ?OutputStyle $output = null;
40
41    public function __construct()
42    {
43        $this->registerFrameworkTasks();
44
45        $this->registerTasks($this->findTasksInConfig());
46
47        $this->registerTasks($this->findTasksInAppDirectory());
48    }
49
50    public function setOutput(?OutputStyle $output): void
51    {
52        $this->output = $output;
53    }
54
55    /** @return array<class-string<\Hyde\Framework\Features\BuildTasks\PreBuildTask>|class-string<\Hyde\Framework\Features\BuildTasks\PostBuildTask>> */
56    public function getRegisteredTasks(): array
57    {
58        return array_map(fn (BuildTask $task): string => $task::class, array_values($this->buildTasks));
59    }
60
61    public function runPreBuildTasks(): void
62    {
63        foreach ($this->buildTasks as $task) {
64            if ($task instanceof PreBuildTask) {
65                $task->run($this->output);
66            }
67        }
68    }
69
70    public function runPostBuildTasks(): void
71    {
72        foreach ($this->buildTasks as $task) {
73            if ($task instanceof PostBuildTask) {
74                $task->run($this->output);
75            }
76        }
77    }
78
79    /** @param  \Hyde\Framework\Features\BuildTasks\PreBuildTask|\Hyde\Framework\Features\BuildTasks\PostBuildTask|class-string<\Hyde\Framework\Features\BuildTasks\PreBuildTask|\Hyde\Framework\Features\BuildTasks\PostBuildTask>  $task */
80    public function registerTask(PreBuildTask|PostBuildTask|string $task): void
81    {
82        $this->registerTaskInService(is_string($task) ? new $task() : $task);
83    }
84
85    protected function registerTaskInService(PreBuildTask|PostBuildTask $task): void
86    {
87        $this->buildTasks[$this->makeTaskIdentifier($task)] = $task;
88    }
89
90    /** @param class-string<\Hyde\Framework\Features\BuildTasks\PreBuildTask|\Hyde\Framework\Features\BuildTasks\PostBuildTask> $task */
91    protected function registerIf(string $task, bool $condition): void
92    {
93        if ($condition) {
94            $this->registerTask($task);
95        }
96    }
97
98    /** @param array<\Hyde\Framework\Features\BuildTasks\PreBuildTask|\Hyde\Framework\Features\BuildTasks\PostBuildTask|class-string<\Hyde\Framework\Features\BuildTasks\PreBuildTask|\Hyde\Framework\Features\BuildTasks\PostBuildTask>> $tasks */
99    protected function registerTasks(array $tasks): void
100    {
101        foreach ($tasks as $task) {
102            $this->registerTask($task);
103        }
104    }
105
106    /** @return array<class-string<\Hyde\Framework\Features\BuildTasks\PreBuildTask>|class-string<\Hyde\Framework\Features\BuildTasks\PostBuildTask>> */
107    protected function findTasksInConfig(): array
108    {
109        return Config::getArray('hyde.build_tasks', []);
110    }
111
112    /** @return array<class-string<\Hyde\Framework\Features\BuildTasks\PreBuildTask>|class-string<\Hyde\Framework\Features\BuildTasks\PostBuildTask>> */
113    protected function findTasksInAppDirectory(): array
114    {
115        return Filesystem::smartGlob('app/Actions/*BuildTask.php')->map(function (string $file): string {
116            return static::pathToClassName($file);
117        })->toArray();
118    }
119
120    protected static function pathToClassName(string $file): string
121    {
122        return str_replace(['app', '.php', '/'], ['App', '', '\\'], $file);
123    }
124
125    protected function makeTaskIdentifier(BuildTask $class): string
126    {
127        // If a user-land task is registered with the same class name (excluding namespaces) as a framework task,
128        // this will allow the user-land task to override the framework task, making them easy to swap out.
129
130        return Str::kebab(class_basename($class));
131    }
132
133    private function registerFrameworkTasks(): void
134    {
135        $this->registerIf(CleanSiteDirectory::class, $this->canCleanSiteDirectory());
136        $this->registerIf(GenerateBuildManifest::class, $this->canGenerateManifest());
137        $this->registerIf(GenerateSitemap::class, $this->canGenerateSitemap());
138        $this->registerIf(GenerateRssFeed::class, $this->canGenerateFeed());
139        $this->registerIf(GenerateSearch::class, $this->canGenerateSearch());
140    }
141
142    private function canCleanSiteDirectory(): bool
143    {
144        return Config::getBool('hyde.empty_output_directory', true);
145    }
146
147    private function canGenerateManifest(): bool
148    {
149        return Config::getBool('hyde.generate_build_manifest', true);
150    }
151
152    private function canGenerateSitemap(): bool
153    {
154        return Features::sitemap();
155    }
156
157    private function canGenerateFeed(): bool
158    {
159        return Features::rss();
160    }
161
162    private function canGenerateSearch(): bool
163    {
164        return Features::hasDocumentationSearch();
165    }
166}