Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
14 / 14
CRAP
100.00% covered (success)
100.00%
1 / 1
CreatesNewPageSourceFile
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
14 / 14
19
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 save
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 parseTitle
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fileName
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 normalizeSubdirectory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 makeOutputPath
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createBladeFile
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 createMarkdownFile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createDocumentationFile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 formatIdentifier
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 validateType
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 failIfFileCannotBeSaved
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 getBladePageContent
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getMarkdownPageContent
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\Actions;
6
7use Hyde\Hyde;
8use Hyde\Pages\BladePage;
9use Illuminate\Support\Str;
10use Hyde\Pages\MarkdownPage;
11use Hyde\Pages\DocumentationPage;
12use Hyde\Framework\Exceptions\FileConflictException;
13use Hyde\Framework\Concerns\InteractsWithDirectories;
14use Hyde\Framework\Exceptions\UnsupportedPageTypeException;
15
16use function trim;
17use function sprintf;
18use function file_put_contents;
19use function file_exists;
20use function basename;
21use function in_array;
22use function str_contains;
23use function Hyde\unslash;
24use function rtrim;
25
26/**
27 * Scaffold a new Markdown, Blade, or documentation page.
28 */
29class CreatesNewPageSourceFile
30{
31    use InteractsWithDirectories;
32
33    /** @var class-string<\Hyde\Pages\Concerns\HydePage> */
34    protected string $pageClass;
35
36    protected string $title;
37    protected string $filename;
38    protected string $outputPath;
39    protected string $subDir = '';
40    protected bool $force;
41
42    protected ?string $customContent;
43
44    public function __construct(string $title, string $pageClass = MarkdownPage::class, bool $force = false, ?string $customContent = null)
45    {
46        $this->validateType($pageClass);
47        $this->pageClass = $pageClass;
48
49        $this->title = $this->parseTitle($title);
50        $this->filename = $this->fileName($title);
51        $this->force = $force;
52        $this->customContent = $customContent;
53
54        $this->outputPath = $this->makeOutputPath($pageClass);
55    }
56
57    public function save(): string
58    {
59        $this->failIfFileCannotBeSaved($this->outputPath);
60
61        match ($this->pageClass) {
62            BladePage::class => $this->createBladeFile(),
63            MarkdownPage::class => $this->createMarkdownFile(),
64            DocumentationPage::class => $this->createDocumentationFile(),
65        };
66
67        return $this->outputPath;
68    }
69
70    protected function parseTitle(string $title): string
71    {
72        return Str::afterLast($title, '/');
73    }
74
75    protected function fileName(string $title): string
76    {
77        // If title contains a slash, it's a subdirectory
78        if (str_contains($title, '/')) {
79            // So we normalize the subdirectory name
80            $this->subDir = $this->normalizeSubdirectory($title);
81        }
82
83        // And return a slug made from just the title without the subdirectory
84        return Str::slug(basename($title));
85    }
86
87    protected function normalizeSubdirectory(string $title): string
88    {
89        return unslash('/'.rtrim(Str::beforeLast($title, '/').'/', '/\\'));
90    }
91
92    /** @param class-string<\Hyde\Pages\Concerns\HydePage> $pageClass */
93    protected function makeOutputPath(string $pageClass): string
94    {
95        return Hyde::path($pageClass::sourcePath($this->formatIdentifier()));
96    }
97
98    protected function createBladeFile(): void
99    {
100        $this->needsParentDirectory($this->outputPath);
101
102        file_put_contents($this->outputPath, Hyde::normalizeNewlines(<<<BLADE
103            @extends('hyde::layouts.app')
104            @section('content')
105            @php(\$title = "$this->title")
106
107            <main class="mx-auto max-w-7xl py-16 px-8">
108                {$this->getBladePageContent()}
109            </main>
110
111            @endsection
112
113            BLADE
114        ));
115    }
116
117    protected function createMarkdownFile(): void
118    {
119        (new MarkdownPage($this->formatIdentifier(), ['title' => $this->title], $this->getMarkdownPageContent()))->save();
120    }
121
122    protected function createDocumentationFile(): void
123    {
124        (new DocumentationPage($this->formatIdentifier(), [], "$this->title"))->save();
125    }
126
127    protected function formatIdentifier(): string
128    {
129        return "$this->subDir/$this->filename";
130    }
131
132    protected function validateType(string $pageClass): void
133    {
134        if (! in_array($pageClass, [MarkdownPage::class, BladePage::class, DocumentationPage::class])) {
135            throw new UnsupportedPageTypeException('The page type must be either "markdown", "blade", or "documentation"');
136        }
137    }
138
139    protected function failIfFileCannotBeSaved(string $path): void
140    {
141        if ($this->force !== true && file_exists($path)) {
142            throw new FileConflictException($path);
143        }
144    }
145
146    protected function getBladePageContent(): string
147    {
148        $baseContent = "<h1 class=\"text-center text-3xl font-bold\">$this->title</h1>";
149
150        return $this->customContent
151            ? trim(sprintf("%s\n\n    <div>\n        %s\n    </div>", $baseContent, $this->customContent))
152            : $baseContent;
153    }
154
155    protected function getMarkdownPageContent(): string
156    {
157        return trim(sprintf("$this->title\n\n%s", $this->customContent ?? ''));
158    }
159}