diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e12925c1a1..7f5afd7811 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.4', '8.0', '8.1', '8.2', '8.3'] + php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] stable: [true] coverage: [true] composer-flags: [''] @@ -42,7 +42,7 @@ jobs: stable: true coverage: false composer-flags: '--prefer-lowest' - - php: '8.4' + - php: '8.5' stable: false coverage: false composer-flags: '--ignore-platform-req=php' @@ -114,7 +114,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index dd29b5e2dc..e5daacb694 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ Updates should follow the [Keep a CHANGELOG](https://keepachangelog.com/) princi ## [Unreleased][unreleased] +## [2.6.1] - 2024-12-29 + +### Fixed + +- Rendered list items should only add newlines around block-level children (#1059, #1061) + ## [2.6.0] - 2024-12-07 This is a **security release** to address potential denial of service attacks when parsing specially crafted, @@ -677,7 +683,8 @@ No changes were introduced since the previous release. - Alternative 1: Use `CommonMarkConverter` or `GithubFlavoredMarkdownConverter` if you don't need to customize the environment - Alternative 2: Instantiate a new `Environment` and add the necessary extensions yourself -[unreleased]: https://github.com/thephpleague/commonmark/compare/2.6.0...main +[unreleased]: https://github.com/thephpleague/commonmark/compare/2.6.1...main +[2.6.1]: https://github.com/thephpleague/commonmark/compare/2.6.0...2.6.1 [2.6.0]: https://github.com/thephpleague/commonmark/compare/2.5.3...2.6.0 [2.5.3]: https://github.com/thephpleague/commonmark/compare/2.5.2...2.5.3 [2.5.2]: https://github.com/thephpleague/commonmark/compare/2.5.1...2.5.2 diff --git a/src/Extension/CommonMark/Renderer/Block/ListItemRenderer.php b/src/Extension/CommonMark/Renderer/Block/ListItemRenderer.php index 570f5a7f76..543baad83d 100644 --- a/src/Extension/CommonMark/Renderer/Block/ListItemRenderer.php +++ b/src/Extension/CommonMark/Renderer/Block/ListItemRenderer.php @@ -17,8 +17,9 @@ namespace League\CommonMark\Extension\CommonMark\Renderer\Block; use League\CommonMark\Extension\CommonMark\Node\Block\ListItem; -use League\CommonMark\Extension\TaskList\TaskListItemMarker; +use League\CommonMark\Node\Block\AbstractBlock; use League\CommonMark\Node\Block\Paragraph; +use League\CommonMark\Node\Block\TightBlockInterface; use League\CommonMark\Node\Node; use League\CommonMark\Renderer\ChildNodeRendererInterface; use League\CommonMark\Renderer\NodeRendererInterface; @@ -39,11 +40,14 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \ ListItem::assertInstanceOf($node); $contents = $childRenderer->renderNodes($node->children()); - if (\substr($contents, 0, 1) === '<' && ! $this->startsTaskListItem($node)) { + + $inTightList = ($parent = $node->parent()) && $parent instanceof TightBlockInterface && $parent->isTight(); + + if ($this->needsBlockSeparator($node->firstChild(), $inTightList)) { $contents = "\n" . $contents; } - if (\substr($contents, -1, 1) === '>') { + if ($this->needsBlockSeparator($node->lastChild(), $inTightList)) { $contents .= "\n"; } @@ -65,10 +69,12 @@ public function getXmlAttributes(Node $node): array return []; } - private function startsTaskListItem(ListItem $block): bool + private function needsBlockSeparator(?Node $child, bool $inTightList): bool { - $firstChild = $block->firstChild(); + if ($child instanceof Paragraph && $inTightList) { + return false; + } - return $firstChild instanceof Paragraph && $firstChild->firstChild() instanceof TaskListItemMarker; + return $child instanceof AbstractBlock; } } diff --git a/tests/functional/CMarkRegressionTest.php b/tests/functional/CMarkRegressionTest.php index 3ebd402fb0..4a5d5f12d8 100644 --- a/tests/functional/CMarkRegressionTest.php +++ b/tests/functional/CMarkRegressionTest.php @@ -27,12 +27,6 @@ public static function dataProvider(): \Generator { $tests = SpecReader::readFile(__DIR__ . '/../../vendor/commonmark/cmark/test/regression.txt'); foreach ($tests as $example) { - // We can't currently render spec example 13 exactly how the upstream library does. We'll likely need to overhaul - // our rendering approach in order to fix that, so we'll use this temporary workaround for now. - if ($example['number'] === 13) { - $example['output'] = \str_replace('', "\n", $example['output']); - } - // The case-fold test from example 21 fails on PHP 8.0.* and below due to the behavior of mb_convert_case(). // See https://3v4l.org/7TeXJ. if (\PHP_VERSION_ID < 81000 && $example['number'] === 21) { diff --git a/tests/functional/CommonMarkJSRegressionTest.php b/tests/functional/CommonMarkJSRegressionTest.php index 64cce72710..ce58efddee 100644 --- a/tests/functional/CommonMarkJSRegressionTest.php +++ b/tests/functional/CommonMarkJSRegressionTest.php @@ -25,17 +25,6 @@ final class CommonMarkJSRegressionTest extends AbstractSpecTestCase { public static function dataProvider(): \Generator { - $tests = SpecReader::readFile(__DIR__ . '/../../vendor/commonmark/commonmark.js/test/regression.txt'); - foreach ($tests as $example) { - // We can't currently render spec examples 18 or 24 exactly how the upstream library does. We'll likely need to overhaul - // our rendering approach in order to fix that, so we'll use this temporary workaround for now. - if ($example['number'] === 18) { - $example['output'] = \str_replace('', "\n", $example['output']); - } elseif ($example['number'] === 24) { - $example['output'] = \str_replace("
The following line is part of HTML block.\n\n", "The following line is part of HTML block.\n", $example['output']); - } - - yield $example; - } + yield from SpecReader::readFile(__DIR__ . '/../../vendor/commonmark/commonmark.js/test/regression.txt'); } } diff --git a/tests/functional/Delimiter/DelimiterProcessingTest.php b/tests/functional/Delimiter/DelimiterProcessingTest.php index 87a7b7a83c..72c0b2f438 100644 --- a/tests/functional/Delimiter/DelimiterProcessingTest.php +++ b/tests/functional/Delimiter/DelimiterProcessingTest.php @@ -49,7 +49,7 @@ public function testAsymmetricDelimiterProcessing(string $input, string $expecte $converter = new MarkdownConverter($e); - $this->assertEquals($expected, $converter->convert($input)); + $this->assertEquals($expected, $converter->convert($input)->getContent()); } /** diff --git a/tests/functional/Extension/Autolink/UrlAutolinkParserTest.php b/tests/functional/Extension/Autolink/UrlAutolinkParserTest.php index 8be6b9fac3..e84cbd694f 100644 --- a/tests/functional/Extension/Autolink/UrlAutolinkParserTest.php +++ b/tests/functional/Extension/Autolink/UrlAutolinkParserTest.php @@ -49,7 +49,7 @@ public static function dataProviderForAutolinkTests(): iterable yield ['www.google.com', '']; yield [' http://leadingwhitespace.example.com', '']; yield ['http://trailingwhitespace.example.com ', '']; - yield ['- https://example.com/list-item', "
google.com is missing www and/or a protocol
']; @@ -60,7 +60,7 @@ public static function dataProviderForAutolinkTests(): iterable yield ['Maybe you\'re interested in https://www.google.com/search?q=php+commonmark!', 'Maybe you\'re interested in https://www.google.com/search?q=php+commonmark!
']; yield ['Or perhaps you\'re looking for my personal website https://www.colinodell.com...?', 'Or perhaps you\'re looking for my personal website https://www.colinodell.com...?
']; yield ['Check https://www.stackoverflow.com: they have all the answers', 'Check https://www.stackoverflow.com: they have all the answers
']; - yield ['- https://example.com/list-item-with-trailing-colon:', ""]; + yield ['- https://example.com/list-item-with-trailing-colon:', ""]; yield ['Visit www.commonmark.org.', 'Visit www.commonmark.org.
']; yield ['Visit www.commonmark.org/a.b.', 'Visit www.commonmark.org/a.b.
']; diff --git a/tests/functional/Extension/Footnote/spec.txt b/tests/functional/Extension/Footnote/spec.txt index 63a090d0f9..0ff84a81f9 100644 --- a/tests/functional/Extension/Footnote/spec.txt +++ b/tests/functional/Extension/Footnote/spec.txt @@ -106,12 +106,9 @@ A [footnote identifier] can come immediately after a word, or have a space betwe [^5]: Not allowed .My Awesome Blog Post
Here's a sample Markdown document
This is my document.
diff --git a/tests/functional/Extension/TableOfContents/md/position-placeholder-multiple.html b/tests/functional/Extension/TableOfContents/md/position-placeholder-multiple.html index 037ebc1863..be9eb609f9 100644 --- a/tests/functional/Extension/TableOfContents/md/position-placeholder-multiple.html +++ b/tests/functional/Extension/TableOfContents/md/position-placeholder-multiple.html @@ -1,28 +1,18 @@This is my document.
Here's a sample Markdown document with lots of headings
Here's a sample Markdown document with weird headings
Here's a sample Markdown document with weird headings
Here's a sample Markdown document with weird headings
Here's a test using <del>
:
And another which does not render the checkbox: