mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-12 10:26:32 +00:00
feature #4890 Add support for tempest/markdown in markdown-extra (ker0x)
This PR was merged into the 3.x branch.
Discussion
----------
Add support for tempest/markdown in markdown-extra
Adds `TempestMarkdown`, an adapter for [`tempest/markdown`](https://github.com/tempestphp/markdown), alongside the existing `LeagueMarkdown`, `MichelfMarkdown` and `ErusevMarkdown` implementations. It follows the same pattern as the others and accepts a pre-configured `Tempest\Markdown\Markdown` in its constructor, so rules and the highlighter can be customized.
It is also appended as the **last** branch of `DefaultMarkdown`'s discovery chain, so projects that already have another library installed keep resolving to it exactly as before.
### PHP requirement
Every published version of `tempest/markdown` requires PHP `^8.5`, while `twig/markdown-extra` supports `>=8.1`. So:
- it is declared in `require-dev` only;
- a CI step removes it before `composer install` on PHP < 8.5, mirroring the existing conditional step used for `twig-extra-bundle`;
- `FunctionalTest` only adds it to the converter matrix when `Tempest\Markdown\Markdown` exists.
The suite passes both with and without the library installed.
### Test data change
Three cases in `getMarkdownTests()` used Setext headings (`Hello` underlined with `=====`). `tempest/markdown` only implements ATX headings, so those were switched to `# Hello`. Those cases exercise the filter plumbing (`{% apply %}`, indentation stripping, `include()|markdown_to_html`) rather than the Markdown dialect, so no coverage is lost.
Two patterns were also relaxed for the same reason: `<h1[^>]*>` because Tempest emits auto heading ids, and `<p>…\s*</p>` because it keeps the source's trailing newline inside the final paragraph. Both remain accurate for the other converters.
These differences, plus the fact that front matter is parsed out rather than rendered, are documented in a note in `doc/filters/markdown_to_html.rst`.
### Unrelated one-liner
The last commit also adds `.php-cs-fixer.cache` to `.gitignore` — it is generated by the project's own `php-cs-fixer` dev dependency and was showing up as untracked. Happy to split it out if you'd rather keep this PR to a single concern.
Commits
-------
aa17f59877 Add support for tempest/markdown in markdown-extra
This commit is contained in:
@@ -151,6 +151,11 @@ jobs:
|
||||
working-directory: extra/${{ matrix.extension }}
|
||||
run: "composer require --no-update 'symfony/translation-contracts:^1.1|^2.0'"
|
||||
|
||||
- name: "Prevent installing tempest/markdown, which requires PHP 8.5"
|
||||
if: "matrix.extension == 'markdown-extra' && matrix.php-version != '8.5'"
|
||||
working-directory: extra/${{ matrix.extension }}
|
||||
run: composer remove --dev --no-update tempest/markdown
|
||||
|
||||
- name: "Composer install ${{ matrix.extension }}"
|
||||
working-directory: extra/${{ matrix.extension }}
|
||||
run: composer install
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
/composer.lock
|
||||
/phpunit.xml
|
||||
/vendor
|
||||
.php-cs-fixer.cache
|
||||
.phpunit.result.cache
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# 3.29.0 (2026-XX-XX)
|
||||
|
||||
* Add `TempestMarkdown` to use `tempest/markdown` as the `markdown_to_html` converter
|
||||
* Fix imported macros not resolving their own template-level macro imports
|
||||
* Add the `Twig\Sandbox\SandboxInterface` interface and `Twig\Sandbox\Sandbox` class to render untrusted templates through a dedicated, always-sandboxed environment crafted for it
|
||||
* Add the `Twig\Extension\SandboxBridgeExtension` to render sandboxed templates from trusted templates with an explicit output escaping strategy
|
||||
|
||||
@@ -84,10 +84,11 @@ Using a Custom Converter
|
||||
The ``markdown_to_html`` filter delegates the conversion to a class implementing
|
||||
``Twig\Extra\Markdown\MarkdownInterface``. Several implementations are provided:
|
||||
``LeagueMarkdown`` (``league/commonmark``), ``MichelfMarkdown``
|
||||
(``michelf/php-markdown``), and ``ErusevMarkdown`` (``erusev/parsedown``). Each
|
||||
accepts a pre-configured converter in its constructor, so you can tune the
|
||||
underlying library or switch to another implementation (for instance
|
||||
``ParsedownExtra``, which extends ``Parsedown``)::
|
||||
(``michelf/php-markdown``), ``ErusevMarkdown`` (``erusev/parsedown``), and
|
||||
``TempestMarkdown`` (``tempest/markdown``). Each accepts a pre-configured
|
||||
converter in its constructor, so you can tune the underlying library or switch
|
||||
to another implementation (for instance ``ParsedownExtra``, which extends
|
||||
``Parsedown``)::
|
||||
|
||||
use Twig\Extra\Markdown\ErusevMarkdown;
|
||||
|
||||
@@ -96,6 +97,14 @@ underlying library or switch to another implementation (for instance
|
||||
|
||||
$markdown = new ErusevMarkdown($parsedown);
|
||||
|
||||
.. note::
|
||||
|
||||
``tempest/markdown`` requires PHP 8.5 or later. It also differs from the
|
||||
other libraries on two points worth knowing about: Setext headings
|
||||
(``Title`` underlined with ``===``) are not supported, so use ATX headings
|
||||
(``# Title``) instead, and any YAML front matter is parsed out of the
|
||||
rendered HTML rather than being rendered.
|
||||
|
||||
When using ``twig/extra-bundle``, register your converter as the
|
||||
``twig.markdown.default`` service to make it the one used by the filter:
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Twig\Extra\Markdown;
|
||||
|
||||
use League\CommonMark\CommonMarkConverter;
|
||||
use Michelf\MarkdownExtra;
|
||||
use Tempest\Markdown\Markdown;
|
||||
|
||||
class DefaultMarkdown implements MarkdownInterface
|
||||
{
|
||||
@@ -26,6 +27,8 @@ class DefaultMarkdown implements MarkdownInterface
|
||||
$this->converter = new MichelfMarkdown();
|
||||
} elseif (class_exists(\Parsedown::class)) {
|
||||
$this->converter = new ErusevMarkdown();
|
||||
} elseif (class_exists(Markdown::class)) {
|
||||
$this->converter = new TempestMarkdown();
|
||||
} else {
|
||||
throw new \LogicException('You cannot use the "markdown_to_html" filter as no Markdown library is available; try running "composer require league/commonmark".');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Extra\Markdown;
|
||||
|
||||
use Tempest\Markdown\Markdown;
|
||||
|
||||
class TempestMarkdown implements MarkdownInterface
|
||||
{
|
||||
private $converter;
|
||||
|
||||
public function __construct(?Markdown $converter = null)
|
||||
{
|
||||
$this->converter = $converter ?: new Markdown();
|
||||
}
|
||||
|
||||
public function convert(string $body): string
|
||||
{
|
||||
return $this->converter->parse($body)->html;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ use Twig\Extra\Markdown\MarkdownExtension;
|
||||
use Twig\Extra\Markdown\MarkdownInterface;
|
||||
use Twig\Extra\Markdown\MarkdownRuntime;
|
||||
use Twig\Extra\Markdown\MichelfMarkdown;
|
||||
use Twig\Extra\Markdown\TempestMarkdown;
|
||||
use Twig\Loader\ArrayLoader;
|
||||
use Twig\RuntimeLoader\RuntimeLoaderInterface;
|
||||
|
||||
@@ -30,12 +31,16 @@ class FunctionalTest extends TestCase
|
||||
*/
|
||||
public function testMarkdown(string $template, string $expected): void
|
||||
{
|
||||
foreach ([LeagueMarkdown::class, ErusevMarkdown::class, /* MichelfMarkdown::class, */ DefaultMarkdown::class] as $class) {
|
||||
$classes = [LeagueMarkdown::class, ErusevMarkdown::class, /* MichelfMarkdown::class, */ DefaultMarkdown::class];
|
||||
if (class_exists(\Tempest\Markdown\Markdown::class)) {
|
||||
$classes[] = TempestMarkdown::class;
|
||||
}
|
||||
|
||||
foreach ($classes as $class) {
|
||||
$twig = new Environment(new ArrayLoader([
|
||||
'index' => $template,
|
||||
'html' => <<<EOF
|
||||
Hello
|
||||
=====
|
||||
# Hello
|
||||
|
||||
Great!
|
||||
EOF,
|
||||
@@ -63,21 +68,19 @@ EOF,
|
||||
return [
|
||||
[<<<EOF
|
||||
{% apply markdown_to_html %}
|
||||
Hello
|
||||
=====
|
||||
# Hello
|
||||
|
||||
Great!
|
||||
{% endapply %}
|
||||
EOF, "<h1>Hello</h1>\n+<p>Great!</p>"],
|
||||
EOF, "<h1[^>]*>Hello</h1>\n+<p>Great!\s*</p>"],
|
||||
[<<<EOF
|
||||
{% apply markdown_to_html %}
|
||||
Hello
|
||||
=====
|
||||
# Hello
|
||||
|
||||
Great!
|
||||
{% endapply %}
|
||||
EOF, "<h1>Hello</h1>\n+<p>Great!</p>"],
|
||||
["{{ include('html')|markdown_to_html }}", "<h1>Hello</h1>\n+<p>Great!</p>"],
|
||||
EOF, "<h1[^>]*>Hello</h1>\n+<p>Great!\s*</p>"],
|
||||
["{{ include('html')|markdown_to_html }}", "<h1[^>]*>Hello</h1>\n+<p>Great!\s*</p>"],
|
||||
[<<<EOF
|
||||
{% apply markdown_to_html %}
|
||||
|
||||
@@ -85,7 +88,7 @@ Paragraph 1
|
||||
|
||||
Paragraph 2
|
||||
{% endapply %}
|
||||
EOF, "<p>Paragraph 1</p>\n+<p>Paragraph 2</p>"],
|
||||
EOF, "<p>Paragraph 1</p>\n+<p>Paragraph 2\s*</p>"],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
"erusev/parsedown": "dev-master as 1.x-dev",
|
||||
"league/commonmark": "^2.7",
|
||||
"league/html-to-markdown": "^4.8|^5.0",
|
||||
"michelf/php-markdown": "^1.8|^2.0"
|
||||
"michelf/php-markdown": "^1.8|^2.0",
|
||||
"tempest/markdown": "^1.2"
|
||||
},
|
||||
"autoload": {
|
||||
"files": [ "Resources/functions.php" ],
|
||||
|
||||
Reference in New Issue
Block a user