From 3f55613faac2cb5078aa4f9e210ecaeda1acfa33 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Thu, 17 Oct 2024 22:59:18 +0200 Subject: [PATCH] [FEAT] add support for front matter Front matter format allows us to add extra meta data to documents like we can with fields in RST. This is a first setup that demos the way we can implement extra fields. --- composer.lock | 77 ++++++++++++++++++- packages/guides-markdown/composer.json | 3 +- .../resources/config/guides-markdown.php | 9 +++ .../src/Markdown/MarkupLanguageParser.php | 3 + .../guides-markdown/src/Markdown/NullNode.php | 20 +++++ .../Parsers/FrontMatter/AuthorParser.php | 33 ++++++++ .../Markdown/Parsers/FrontMatter/Parser.php | 24 ++++++ .../Parsers/FrontMatter/TitleParser.php | 30 ++++++++ .../Markdown/Parsers/FrontMatterParser.php | 65 ++++++++++++++++ .../front-matter-md/expected/index.html | 61 +++++++++++++++ .../front-matter-md/input/guides.xml | 11 +++ .../front-matter-md/input/index.md | 8 ++ 12 files changed, 341 insertions(+), 3 deletions(-) create mode 100644 packages/guides-markdown/src/Markdown/NullNode.php create mode 100644 packages/guides-markdown/src/Markdown/Parsers/FrontMatter/AuthorParser.php create mode 100644 packages/guides-markdown/src/Markdown/Parsers/FrontMatter/Parser.php create mode 100644 packages/guides-markdown/src/Markdown/Parsers/FrontMatter/TitleParser.php create mode 100644 packages/guides-markdown/src/Markdown/Parsers/FrontMatterParser.php create mode 100644 tests/Integration/tests-full/markdown-full/front-matter-md/expected/index.html create mode 100644 tests/Integration/tests-full/markdown-full/front-matter-md/input/guides.xml create mode 100644 tests/Integration/tests-full/markdown-full/front-matter-md/input/index.md diff --git a/composer.lock b/composer.lock index eb615a2be..07cdea1ac 100644 --- a/composer.lock +++ b/composer.lock @@ -1481,12 +1481,13 @@ "dist": { "type": "path", "url": "./packages/guides-markdown", - "reference": "ea838b19997533272257f99ab9f96d29ab58b8d0" + "reference": "1b539ac52549d0ad20f86416df2d526a8ce70270" }, "require": { "league/commonmark": "^2.4", "php": "^8.1", - "phpdocumentor/guides": "^1.0 || ^2.0" + "phpdocumentor/guides": "^1.0 || ^2.0", + "symfony/yaml": "^6.4 || ^7.0" }, "type": "library", "extra": { @@ -3804,6 +3805,78 @@ ], "time": "2024-04-18T09:22:46+00:00" }, + { + "name": "symfony/yaml", + "version": "v6.4.12", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "762ee56b2649659380e0ef4d592d807bc17b7971" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/762ee56b2649659380e0ef4d592d807bc17b7971", + "reference": "762ee56b2649659380e0ef4d592d807bc17b7971", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v6.4.12" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-17T12:47:12+00:00" + }, { "name": "twig/twig", "version": "v3.14.0", diff --git a/packages/guides-markdown/composer.json b/packages/guides-markdown/composer.json index 13008589b..b1f257f51 100644 --- a/packages/guides-markdown/composer.json +++ b/packages/guides-markdown/composer.json @@ -23,7 +23,8 @@ "require": { "php": "^8.1", "league/commonmark": "^2.4", - "phpdocumentor/guides": "^1.0 || ^2.0" + "phpdocumentor/guides": "^1.0 || ^2.0", + "symfony/yaml": "^6.4 || ^7.0" }, "extra": { "branch-alias": { diff --git a/packages/guides-markdown/resources/config/guides-markdown.php b/packages/guides-markdown/resources/config/guides-markdown.php index 49edb3f7f..cfd4e4bc2 100644 --- a/packages/guides-markdown/resources/config/guides-markdown.php +++ b/packages/guides-markdown/resources/config/guides-markdown.php @@ -5,6 +5,8 @@ use phpDocumentor\Guides\Markdown\MarkupLanguageParser; use phpDocumentor\Guides\Markdown\Parsers\BlockQuoteParser; use phpDocumentor\Guides\Markdown\Parsers\CodeBlockParser; +use phpDocumentor\Guides\Markdown\Parsers\FrontMatter\TitleParser; +use phpDocumentor\Guides\Markdown\Parsers\FrontMatterParser; use phpDocumentor\Guides\Markdown\Parsers\HeaderParser; use phpDocumentor\Guides\Markdown\Parsers\HtmlParser; use phpDocumentor\Guides\Markdown\Parsers\InlineParsers\EmphasisParser; @@ -82,6 +84,13 @@ ->set(NewLineParser::class) ->tag('phpdoc.guides.markdown.parser.inlineParser') + ->set(FrontMatterParser::class) + ->arg('$fieldParsers', tagged_iterator('phpdoc.guides.markdown.front_matter', 'fieldName')) + ->tag('phpdoc.guides.markdown.parser.blockParser') + + ->set(TitleParser::class) + ->tag('phpdoc.guides.markdown.front_matter') + ->set(MarkupLanguageParser::class) ->arg('$parsers', tagged_iterator('phpdoc.guides.markdown.parser.blockParser')) ->tag('phpdoc.guides.parser.markupLanguageParser'); diff --git a/packages/guides-markdown/src/Markdown/MarkupLanguageParser.php b/packages/guides-markdown/src/Markdown/MarkupLanguageParser.php index e92b8ee1d..5bbbf1f0b 100644 --- a/packages/guides-markdown/src/Markdown/MarkupLanguageParser.php +++ b/packages/guides-markdown/src/Markdown/MarkupLanguageParser.php @@ -16,6 +16,8 @@ use League\CommonMark\Environment\Environment as CommonMarkEnvironment; use League\CommonMark\Extension\Autolink\AutolinkExtension; use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension; +use League\CommonMark\Extension\FrontMatter\Data\SymfonyYamlFrontMatterParser; +use League\CommonMark\Extension\FrontMatter\FrontMatterExtension; use League\CommonMark\Extension\Table\TableExtension; use League\CommonMark\Node\Block\Document; use League\CommonMark\Node\NodeWalker; @@ -54,6 +56,7 @@ public function __construct( $cmEnvironment->addExtension(new CommonMarkCoreExtension()); $cmEnvironment->addExtension(new TableExtension()); $cmEnvironment->addExtension(new AutolinkExtension()); + $cmEnvironment->addExtension(new FrontMatterExtension(new SymfonyYamlFrontMatterParser())); $this->markdownParser = new MarkdownParser($cmEnvironment); // if for backward compatibility reasons no settings manager was passed, use the defaults $this->settingsManager = $settingsManager ?? new SettingsManager(new ProjectSettings()); diff --git a/packages/guides-markdown/src/Markdown/NullNode.php b/packages/guides-markdown/src/Markdown/NullNode.php new file mode 100644 index 000000000..adba51403 --- /dev/null +++ b/packages/guides-markdown/src/Markdown/NullNode.php @@ -0,0 +1,20 @@ +addHeaderNode(new AuthorNode($value, [new PlainTextInlineNode($value)])); + } + + public function field(): string + { + return 'title'; + } +} diff --git a/packages/guides-markdown/src/Markdown/Parsers/FrontMatter/Parser.php b/packages/guides-markdown/src/Markdown/Parsers/FrontMatter/Parser.php new file mode 100644 index 000000000..dcf11d985 --- /dev/null +++ b/packages/guides-markdown/src/Markdown/Parsers/FrontMatter/Parser.php @@ -0,0 +1,24 @@ + $frontMatter */ + public function process(DocumentNode $document, mixed $value, array $frontMatter): void; +} diff --git a/packages/guides-markdown/src/Markdown/Parsers/FrontMatter/TitleParser.php b/packages/guides-markdown/src/Markdown/Parsers/FrontMatter/TitleParser.php new file mode 100644 index 000000000..6c1616ca0 --- /dev/null +++ b/packages/guides-markdown/src/Markdown/Parsers/FrontMatter/TitleParser.php @@ -0,0 +1,30 @@ +setMetaTitle('' . $value); + } + + public function field(): string + { + return 'title'; + } +} diff --git a/packages/guides-markdown/src/Markdown/Parsers/FrontMatterParser.php b/packages/guides-markdown/src/Markdown/Parsers/FrontMatterParser.php new file mode 100644 index 000000000..59db2f04a --- /dev/null +++ b/packages/guides-markdown/src/Markdown/Parsers/FrontMatterParser.php @@ -0,0 +1,65 @@ + */ +final class FrontMatterParser implements ParserInterface +{ + /** @var array */ + private array $fieldParsers; + + /** @param iterable $fieldParsers */ + public function __construct(iterable $fieldParsers) + { + foreach ($fieldParsers as $parser) { + $this->fieldParsers[$parser->field()] = $parser; + } + } + + public function parse(GuidesParser $parser, NodeWalker $walker, CommonMarkNode $current): Node + { + $frontMatter = $current->data->get('front_matter', []); + if (is_array($frontMatter) === false) { + return new NullNode(''); + } + + foreach ($frontMatter as $field => $value) { + if (!array_key_exists($field, $this->fieldParsers)) { + continue; + } + + $this->fieldParsers[$field]->process($parser->getDocument(), $value, $frontMatter); + } + + return new NullNode(''); + } + + public function supports(NodeWalkerEvent $event): bool + { + return $event->getNode() instanceof Document; + } +} diff --git a/tests/Integration/tests-full/markdown-full/front-matter-md/expected/index.html b/tests/Integration/tests-full/markdown-full/front-matter-md/expected/index.html new file mode 100644 index 000000000..cee50f9d1 --- /dev/null +++ b/tests/Integration/tests-full/markdown-full/front-matter-md/expected/index.html @@ -0,0 +1,61 @@ + + + + Front Matter + + + + + + +
+ +
+
+
+
+
+
+ +
+
+ + +
+

Document Title

+

Lorem Ipsum Dolor.

+
+ +
+
+
+
+
+ + + + + + + diff --git a/tests/Integration/tests-full/markdown-full/front-matter-md/input/guides.xml b/tests/Integration/tests-full/markdown-full/front-matter-md/input/guides.xml new file mode 100644 index 000000000..4759d605e --- /dev/null +++ b/tests/Integration/tests-full/markdown-full/front-matter-md/input/guides.xml @@ -0,0 +1,11 @@ + + + + diff --git a/tests/Integration/tests-full/markdown-full/front-matter-md/input/index.md b/tests/Integration/tests-full/markdown-full/front-matter-md/input/index.md new file mode 100644 index 000000000..23363143c --- /dev/null +++ b/tests/Integration/tests-full/markdown-full/front-matter-md/input/index.md @@ -0,0 +1,8 @@ +--- +title: Front Matter +non-existing: Front Matter Description +author: phpdoc +--- +# Document Title + +Lorem Ipsum Dolor.