diff --git a/config/services.yaml b/config/services.yaml index 9113951..7edcc9c 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -82,10 +82,14 @@ services: class: Sigwin\YASSG\Bridge\Symfony\HttpKernel\Fragment\RelativeUrlInlineFragmentRenderer decorates: fragment.renderer.inline - sigwin_yassg.file_decoder.caching_file_decoder: + sigwin_yassg.file_decoder.thumbnail_queue_file_decoder: class: Sigwin\YASSG\Decoder\CachingFileDecoder decorates: 'Sigwin\YASSG\FileDecoder' - + decoration_priority: 200 + sigwin_yassg.file_decoder.caching_file_decoder: + class: Sigwin\YASSG\Decoder\ThumbnailQueueFileDecoder + decorates: 'Sigwin\YASSG\FileDecoder' + decoration_priority: 100 Sigwin\YASSG\FileDecoder: class: Sigwin\YASSG\Decoder\CompositeFileDecoder arguments: diff --git a/psalm.baseline.xml b/psalm.baseline.xml index b23f625..748a7d3 100644 --- a/psalm.baseline.xml +++ b/psalm.baseline.xml @@ -407,6 +407,19 @@ PaginatorExtension + + + + + + + + + + + ThumbnailExtension + + column @@ -536,7 +549,16 @@ MarkdownFileDecoder + + + ThumbnailQueueFileDecoder + + + + $data + array + YamlFileDecoder @@ -648,6 +670,11 @@ array + + + __construct + + EventSubscriber diff --git a/src/Bridge/Twig/Extension/ThumbnailExtension.php b/src/Bridge/Twig/Extension/ThumbnailExtension.php new file mode 100644 index 0000000..39e05b0 --- /dev/null +++ b/src/Bridge/Twig/Extension/ThumbnailExtension.php @@ -0,0 +1,47 @@ +thumbnailQueue->add([ + 'source' => $path, + 'destination' => $relative, + ]); + + return $this->packages->getUrl(ltrim($relative, '/')); + }, ['needs_context' => true]), + ]; + } +} diff --git a/src/Decoder/CachingFileDecoder.php b/src/Decoder/CachingFileDecoder.php index b7d2b28..b234f62 100644 --- a/src/Decoder/CachingFileDecoder.php +++ b/src/Decoder/CachingFileDecoder.php @@ -37,7 +37,7 @@ public function decode(\SplFileInfo $file): array $item = $this->cachePoolItem->getItem($key); if ($item->isHit()) { - /** @var array $value */ + /** @var array $value */ $value = $item->get(); return $value; diff --git a/src/Decoder/MarkdownFileDecoder.php b/src/Decoder/MarkdownFileDecoder.php index c999b21..f230938 100644 --- a/src/Decoder/MarkdownFileDecoder.php +++ b/src/Decoder/MarkdownFileDecoder.php @@ -16,6 +16,7 @@ use League\CommonMark\ConverterInterface; use League\CommonMark\Extension\FrontMatter\FrontMatterProviderInterface; use Sigwin\YASSG\FileDecoder; +use Sigwin\YASSG\ThumbnailQueue; use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Yaml; use Twig\Environment; @@ -26,7 +27,7 @@ private const EXTENSIONS = ['md', 'markdown']; - public function __construct(private ConverterInterface $converter, private Environment $twig) {} + public function __construct(private ConverterInterface $converter, private Environment $twig, private ThumbnailQueue $thumbnailQueue) {} public function decode(\SplFileInfo $file): array { @@ -41,6 +42,7 @@ public function decode(\SplFileInfo $file): array } $metadata = []; + $thumbnails = []; if (str_contains($content, '{{') || str_contains($content, '{%')) { if (str_starts_with($content, '---')) { $end = mb_strpos($content, '---', 3); @@ -59,7 +61,9 @@ public function decode(\SplFileInfo $file): array $content = $this->twig->createTemplate($content)->render([ 'item' => $metadata, + '_path' => $file->getPathname(), ]); + $thumbnails = $this->thumbnailQueue->flush(); } $result = $this->converter->convert($content); @@ -68,6 +72,7 @@ public function decode(\SplFileInfo $file): array $metadata = $result->getFrontMatter(); } $metadata['body'] = $result->getContent(); + $metadata['@thumbnails'] = $thumbnails; return $metadata; } diff --git a/src/Decoder/ThumbnailQueueFileDecoder.php b/src/Decoder/ThumbnailQueueFileDecoder.php new file mode 100644 index 0000000..03fca71 --- /dev/null +++ b/src/Decoder/ThumbnailQueueFileDecoder.php @@ -0,0 +1,45 @@ +decoder->supports($file); + } + + public function decode(\SplFileInfo $file): array + { + /** @var array{"@thumbnails"?: list} $decoded */ + $decoded = $this->decoder->decode($file); + + if (isset($decoded['@thumbnails'])) { + foreach ($decoded['@thumbnails'] as $thumbnail) { + $this->thumbnailQueue->add($thumbnail); + } + } + unset($decoded['@thumbnails']); + + return $decoded; + } +} diff --git a/src/FileDecoder.php b/src/FileDecoder.php index 637c310..3815e65 100644 --- a/src/FileDecoder.php +++ b/src/FileDecoder.php @@ -17,5 +17,8 @@ interface FileDecoder { public function supports(\SplFileInfo $file): bool; + /** + * @return array + */ public function decode(\SplFileInfo $file): array; } diff --git a/src/Generator.php b/src/Generator.php index 3542aba..65a1f0e 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -28,8 +28,13 @@ */ final readonly class Generator { - public function __construct(private string $buildDir, private Permutator $permutator, private UrlGeneratorInterface $urlGenerator, private KernelInterface $kernel, private Filesystem $filesystem) {} + public function __construct(private string $buildDir, private Permutator $permutator, private UrlGeneratorInterface $urlGenerator, private KernelInterface $kernel, private Filesystem $filesystem, private ThumbnailQueue $thumbnailQueue) {} + /** + * @param callable(Request, Response, string): void $callable + * + * @throws \Exception + */ public function generate(callable $callable): void { $requestContext = $this->urlGenerator->getContext(); @@ -71,6 +76,10 @@ public function generate(callable $callable): void $response = $this->dumpRequest($callable, $request); $urlSet->addUrl(new UrlConcrete($url, new \DateTimeImmutable($response->headers->get('Last-Modified', 'now')))); + + $this->thumbnailQueue->flush(function (array $item) use ($callable): void { + $callable($this->createRequest($item['destination']), new Response('OK'), $item['destination']); + }); } if ($urlSet !== null) { $this->dumpSitemap($urlSet, $deflate); @@ -105,6 +114,11 @@ private function generateUrl(string $path): string return sprintf('%1$s://%2$s%3$s%4$s', $context->getScheme(), $context->getHost(), $context->getBaseUrl(), $path); } + /** + * @param callable(Request, Response, string): void $callable + * + * @throws \Exception + */ private function dumpRequest(callable $callable, Request $request, int $expectedStatusCode = 200): Response { try { diff --git a/src/ThumbnailQueue.php b/src/ThumbnailQueue.php new file mode 100644 index 0000000..df44232 --- /dev/null +++ b/src/ThumbnailQueue.php @@ -0,0 +1,62 @@ + + */ + private array $queue = []; + + public function __construct(private string $buildDir, private Filesystem $filesystem) {} + + /** + * @param TThumbnailOptions $specification + */ + public function add(array $specification): void + { + $this->queue[$specification['destination']] = $specification; + } + + /** + * @param callable(TThumbnailOptions): void $callable + * + * @return list + */ + public function flush(?callable $callable = null): array + { + foreach ($this->queue as $specification) { + $destination = $this->buildDir.'/'.ltrim($specification['destination'], '/'); + if (file_exists($destination)) { + continue; + } + + // TODO: ImgProxy + $this->filesystem->copy($specification['source'], $destination); + if ($callable !== null) { + $callable($specification); + } + } + $queue = array_values($this->queue); + $this->queue = []; + + return $queue; + } +} diff --git a/tests/functional/site/content/articles/images/image.webp b/tests/functional/site/content/articles/images/image.webp new file mode 100644 index 0000000..57df74a Binary files /dev/null and b/tests/functional/site/content/articles/images/image.webp differ diff --git a/tests/functional/site/content/articles/images.md b/tests/functional/site/content/articles/images/images.md similarity index 73% rename from tests/functional/site/content/articles/images.md rename to tests/functional/site/content/articles/images/images.md index bfe9f69..00bad3d 100644 --- a/tests/functional/site/content/articles/images.md +++ b/tests/functional/site/content/articles/images/images.md @@ -2,7 +2,7 @@ title: Images! slug: images publishedAt: "2022-07-20 12:35:00" -image: assets/images/sigwin.svg +image: ./image.webp --- | Column 1 | Column 2 | Column 3 | @@ -13,7 +13,7 @@ image: assets/images/sigwin.svg This is a database lookup example: {{yassg_get('articles', '/hello-world.md').title}} -This is an asset lookup: {{asset(item.image)}} +This is an asset lookup: {{ yassg_thumbnail(item.image) }} -![Logo]({{ asset(item.image) }}) +![Logo]({{ yassg_thumbnail(item.image) }}) diff --git a/tests/functional/site/fixtures/content/articles/images/image.webp b/tests/functional/site/fixtures/content/articles/images/image.webp new file mode 100644 index 0000000..57df74a Binary files /dev/null and b/tests/functional/site/fixtures/content/articles/images/image.webp differ diff --git a/tests/functional/site/fixtures/en/article/images/index.html b/tests/functional/site/fixtures/en/article/images/index.html index df7e3d6..83b5492 100644 --- a/tests/functional/site/fixtures/en/article/images/index.html +++ b/tests/functional/site/fixtures/en/article/images/index.html @@ -43,8 +43,8 @@

Images!

This is a database lookup example: Hello World!

-

This is an asset lookup: /sub/dir/another/assets/images/sigwin.6f9a3d5b.svg

-

Logo

+

This is an asset lookup: /sub/dir/another/content/articles/images/image.webp

+

Logo