Skip to content

Commit

Permalink
Front matter support, proper page entity
Browse files Browse the repository at this point in the history
  • Loading branch information
Radiergummi committed Feb 16, 2021
1 parent 40e28d5 commit 54dcb6d
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 54 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
55 changes: 54 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions resources/views/components/page.blade.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<article class="mb-8">
<header class="mx-8 pb-4">
<h2 class="text-3xl leading-relaxed" id="{{ $slug() }}">
<a href="#{{ $slug() }}">{{ $title() }}</a>
<h2 class="text-3xl leading-relaxed" id="{{ $page->slug() }}">
<a href="#{{ $page->slug() }}">{{ $page->title() }}</a>
</h2>
</header>

<div class="mb-4 p-8 text-base leading-relaxed">
<div class="markdown">{!! trim($content()) !!}</div>
<div class="markdown">{!! $content() !!}</div>
</div>
</article>
6 changes: 3 additions & 3 deletions resources/views/index.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@

@section('content')
<article class="container mx-auto py-8">
@foreach($pages as $title => $content)
<x-herodot-page :title="$title" :content="$content"/>
@foreach($pages as $page)
<x-herodot-page :page="$page" />
@endforeach

@foreach ($groups as $name => $endpoints)
<x-herodot-group :name="$name" :endpoints="$endpoints"/>
<x-herodot-group :name="$name" :endpoints="$endpoints" />
@endforeach
</article>
@endsection
Expand Down
21 changes: 11 additions & 10 deletions resources/views/partials/navigation.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@
<span class="material-icons theme-toggle theme-toggle--auto">brightness_auto</span>
</button>
</header>

<div class="relative overflow-y-auto p-4">
<ul>
@foreach ($pages->keys() as $title)
<li class="@if(!$loop->first) mt-4 @endif">
<a href="#{{ Str::slug($title) }}" class="hover:text-blue-500">
{{ $title }}
</a>
</li>
@endforeach
</ul>
@unless ($pages->isEmpty())
<ul class="mb-4">
@foreach ($pages as $page)
<li class="@if(!$loop->first) mt-4 @endif">
<a href="#{{ $page->slug() }}" class="hover:text-blue-500">
{{ $page->title() }}
</a>
</li>
@endforeach
</ul>
@endunless

<ul>
@foreach($groups as $group => $endpoints)
Expand Down
194 changes: 194 additions & 0 deletions src/Entities/Page.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
<?php

declare(strict_types=1);

namespace Matchory\Herodot\Entities;

use DateTimeImmutable;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Str;
use JetBrains\PhpStorm\Pure;
use JsonSerializable;
use Matchory\Herodot\Interfaces\StructureInterface;
use Spatie\YamlFrontMatter\Document;
use Spatie\YamlFrontMatter\Document as FrontMatter;
use Spatie\YamlFrontMatter\YamlFrontMatter;
use SplFileInfo;

use function array_merge;
use function file_get_contents;

class Page implements StructureInterface, JsonSerializable, Arrayable
{
public const FRONT_MATTER_TITLE = 'title';

protected ?string $title = null;

protected ?Document $document = null;

protected ?string $content = null;

final public function __construct(protected SplFileInfo $fileInfo)
{
}

public static function __set_state(array $array): static
{
return new static($array['fileInfo']);
}

#[Pure] public function file(): SplFileInfo
{
return $this->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();
}
}
9 changes: 4 additions & 5 deletions src/Printing/BladePrinter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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);
}

/**
Expand Down
Loading

0 comments on commit 54dcb6d

Please sign in to comment.