diff --git a/composer.json b/composer.json
index ad16e19..61b64f7 100644
--- a/composer.json
+++ b/composer.json
@@ -39,7 +39,8 @@
"cebe/php-openapi": "^1.4",
"illuminate/config": "^8.21",
"illuminate/view": "^8.26",
- "league/commonmark": "^1.5"
+ "league/commonmark": "^1.5",
+ "spatie/yaml-front-matter": "^2.0"
},
"extra": {
"laravel": {
diff --git a/composer.lock b/composer.lock
index 15e64e4..90eb036 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "be668d2d48d5edfbd719ed32fa1c5133",
+ "content-hash": "59a962967faa300a334e0759075be34a",
"packages": [
{
"name": "brick/math",
@@ -1984,6 +1984,59 @@
],
"time": "2020-08-18T17:17:46+00:00"
},
+ {
+ "name": "spatie/yaml-front-matter",
+ "version": "2.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/yaml-front-matter.git",
+ "reference": "efb5170a92216b8d2f40874301db8eb51b8e73cc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/yaml-front-matter/zipball/efb5170a92216b8d2f40874301db8eb51b8e73cc",
+ "reference": "efb5170a92216b8d2f40874301db8eb51b8e73cc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0",
+ "symfony/yaml": "^3.0|^4.0|^5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Spatie\\YamlFrontMatter\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian De Deyne",
+ "email": "sebastian@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
+ }
+ ],
+ "description": "A to the point yaml front matter parser",
+ "homepage": "https://github.com/sebastiandedeyne/yaml-front-matter",
+ "keywords": [
+ "front matter",
+ "jekyll",
+ "spatie",
+ "yaml"
+ ],
+ "support": {
+ "issues": "https://github.com/spatie/yaml-front-matter/issues",
+ "source": "https://github.com/spatie/yaml-front-matter/tree/master"
+ },
+ "time": "2019-12-02T20:50:50+00:00"
+ },
{
"name": "symfony/console",
"version": "v5.2.1",
diff --git a/resources/views/components/page.blade.php b/resources/views/components/page.blade.php
index b0e32a4..bcde2c4 100644
--- a/resources/views/components/page.blade.php
+++ b/resources/views/components/page.blade.php
@@ -1,11 +1,11 @@
-
{!! trim($content()) !!}
+
{!! $content() !!}
diff --git a/resources/views/index.blade.php b/resources/views/index.blade.php
index 115da53..169200e 100644
--- a/resources/views/index.blade.php
+++ b/resources/views/index.blade.php
@@ -13,12 +13,12 @@
@section('content')
- @foreach($pages as $title => $content)
-
+ @foreach($pages as $page)
+
@endforeach
@foreach ($groups as $name => $endpoints)
-
+
@endforeach
@endsection
diff --git a/resources/views/partials/navigation.blade.php b/resources/views/partials/navigation.blade.php
index 9e0d0b3..ff890e1 100644
--- a/resources/views/partials/navigation.blade.php
+++ b/resources/views/partials/navigation.blade.php
@@ -11,17 +11,18 @@
brightness_auto
-
-
+ @unless ($pages->isEmpty())
+
+ @endunless
@foreach($groups as $group => $endpoints)
diff --git a/src/Entities/Page.php b/src/Entities/Page.php
new file mode 100644
index 0000000..02099eb
--- /dev/null
+++ b/src/Entities/Page.php
@@ -0,0 +1,194 @@
+fileInfo;
+ }
+
+ public function extension(): string
+ {
+ return $this->fileInfo->getExtension();
+ }
+
+ public function path(): string
+ {
+ return $this->fileInfo->getPathname();
+ }
+
+ public function filename(): string
+ {
+ return $this->fileInfo->getFilename();
+ }
+
+ public function directory(): string
+ {
+ return $this->fileInfo->getPath();
+ }
+
+ public function fileSize(): int
+ {
+ return $this->fileInfo->getSize();
+ }
+
+ public function modified(): ?DateTimeImmutable
+ {
+ $timestamp = $this->fileInfo->getMTime();
+ $modified = new DateTimeImmutable();
+
+ return $modified->setTimestamp($timestamp) ?: null;
+ }
+
+ public function created(): ?DateTimeImmutable
+ {
+ $timestamp = $this->fileInfo->getCTime();
+ $modified = new DateTimeImmutable();
+
+ return $modified->setTimestamp($timestamp) ?: null;
+ }
+
+ public function title(): string
+ {
+ if ( ! $this->title) {
+ $this->title = $this->inferTitle();
+ }
+
+ return $this->title;
+ }
+
+ public function slug(): string
+ {
+ return Str::slug($this->title());
+ }
+
+ public function raw(): string
+ {
+ if ( ! $this->content) {
+ $this->content = $this->readFileContents();
+ }
+
+ return $this->content;
+ }
+
+ public function content(): string
+ {
+ return $this->document()->body();
+ }
+
+ public function document(): FrontMatter
+ {
+ if ( ! $this->document) {
+ $this->document = $this->load();
+ }
+
+ return $this->document;
+ }
+
+ public function frontMatter(): array
+ {
+ return $this->document()->matter();
+ }
+
+ public function get(string $key): mixed
+ {
+ return $this->document()->matter($key);
+ }
+
+ public function __get(string|int $name): mixed
+ {
+ return $this->get((string)$name);
+ }
+
+ public function __set(string $name, mixed $value): void
+ {
+ // no-op
+ }
+
+ public function __isset(string $name): bool
+ {
+ return $this->get($name) !== null;
+ }
+
+ public function jsonSerialize(): array
+ {
+ return $this->toArray();
+ }
+
+ public function toArray(): array
+ {
+ return array_merge($this->frontMatter(), [
+ 'file' => $this->path(),
+ 'content' => $this->content(),
+ ]);
+ }
+
+ protected function load(): FrontMatter
+ {
+ return YamlFrontMatter::parse($this->raw());
+ }
+
+ protected function readFileContents(): string
+ {
+ return file_get_contents($this->fileInfo->getPathname());
+ }
+
+ protected function inferTitle(): string
+ {
+ if ($title = $this->get(self::FRONT_MATTER_TITLE)) {
+ return $title;
+ }
+
+ // TODO: Check for h1 in content
+
+ return $this->inferTitleFromFileName();
+ }
+
+ protected function inferTitleFromFileName(): string
+ {
+ $extension = $this->fileInfo->getExtension();
+ $basename = $this->fileInfo->getBasename('.' . $extension);
+
+ return (string)Str
+ ::of($basename)
+ ->kebab()
+ ->replace(['-', '_'], ' ')
+ ->title()
+ ->words();
+ }
+}
diff --git a/src/Printing/BladePrinter.php b/src/Printing/BladePrinter.php
index 035f888..f68aa16 100644
--- a/src/Printing/BladePrinter.php
+++ b/src/Printing/BladePrinter.php
@@ -16,6 +16,7 @@
use League\CommonMark\MarkdownConverterInterface;
use Matchory\Herodot\Contracts\Printer;
use Matchory\Herodot\Entities\Endpoint;
+use Matchory\Herodot\Entities\Page;
use SplFileInfo;
use function file_get_contents;
@@ -118,7 +119,7 @@ protected function pagesPath(): ?string
* documentation as generated by the Blade printer. Pages will be added as
* top-level navigation items and allow providing non-code bound content.
*
- * @return Collection Collection of page content mapped to their title.
+ * @return Collection Collection of page instances.
*/
protected function pages(): Collection
{
@@ -154,10 +155,8 @@ protected function pages(): Collection
// Make sure we only load supported files
->filter(fn(SplFileInfo $file) => $this->isPage($file))
- // Map pages to their title
- ->mapWithKeys(fn(SplFileInfo $file): array => [
- $this->pageTitle($file) => $this->pageContent($file),
- ]);
+ // Map the files to pages
+ ->mapInto(Page::class);
}
/**
diff --git a/src/View/Components/Page.php b/src/View/Components/Page.php
index aa6731a..ef829d9 100644
--- a/src/View/Components/Page.php
+++ b/src/View/Components/Page.php
@@ -6,38 +6,18 @@
use Illuminate\Contracts\View\View as ViewContract;
use Illuminate\Support\Facades\View;
-use Illuminate\Support\Str;
-use Illuminate\View\Component;
-use Illuminate\View\ComponentAttributeBag;
use League\CommonMark\MarkdownConverterInterface;
+use Matchory\Herodot\Entities\Page as PageEntity;
use RuntimeException;
-class Page extends Component
+class Page extends AbstractHerodotComponent
{
- /**
- * @var string|null
- */
- public $componentName = null;
-
- /**
- * @var ComponentAttributeBag|null
- */
- public $attributes = null;
-
- protected string $title;
-
- protected string $content;
-
- protected MarkdownConverterInterface $markdownConverter;
+ protected ?string $parsedContent = null;
public function __construct(
- MarkdownConverterInterface $markdownConverter,
- string $title,
- string $content
+ protected MarkdownConverterInterface $markdownConverter,
+ public PageEntity $page
) {
- $this->title = $title;
- $this->content = $content;
- $this->markdownConverter = $markdownConverter;
}
public function render(): ViewContract
@@ -51,19 +31,22 @@ public function render(): ViewContract
*/
public function content(): string
{
- return $this->markdownConverter->convertToHtml(
- $this
- ->content
- );
+ if ( ! $this->parsedContent) {
+ $this->parsedContent = $this->markdownConverter->convertToHtml(
+ $this->page->content()
+ );
+ }
+
+ return $this->parsedContent;
}
public function slug(): string
{
- return Str::slug($this->title);
+ return $this->page->slug();
}
public function title(): string
{
- return $this->title;
+ return $this->page->title();
}
}