diff --git a/src/Rules/HtmlRule.php b/src/Rules/HtmlRule.php index df4bff5..a56e0cc 100644 --- a/src/Rules/HtmlRule.php +++ b/src/Rules/HtmlRule.php @@ -10,6 +10,13 @@ final class HtmlRule implements Rule, ProvidesFirstChar { + /** + * Elements whose content is raw text rather than Markdown. + * + * @see https://spec.commonmark.org/0.31.2/#html-blocks + */ + private const array RAW_TEXT_TAGS = ['pre', 'script', 'style', 'textarea']; + public string $firstChar = '<'; public function shouldParse(Parser $parser): bool @@ -84,6 +91,13 @@ public function parse(Parser $parser): Token $content .= $parser->consumeWhile(Parser::NEW_LINE); - return new HtmlToken($content); + return new HtmlToken( + $content, + raw: in_array( + strtolower($tagName), + self::RAW_TEXT_TAGS, + strict: true, + ), + ); } } diff --git a/src/Tokens/HtmlToken.php b/src/Tokens/HtmlToken.php index 93b0d61..5663e04 100644 --- a/src/Tokens/HtmlToken.php +++ b/src/Tokens/HtmlToken.php @@ -18,10 +18,17 @@ final class HtmlToken implements Token { public function __construct( public string $html, + + /** Whether the content is raw text that must never be parsed as Markdown. */ + public bool $raw = false, ) {} public function parse(Parser $parser): string { + if ($this->raw) { + return $this->html; + } + return $parser ->forToken($this, [ new BoldAndItalicRule(), diff --git a/tests/Rules/HtmlRuleTest.php b/tests/Rules/HtmlRuleTest.php index aee0050..5f8208d 100644 --- a/tests/Rules/HtmlRuleTest.php +++ b/tests/Rules/HtmlRuleTest.php @@ -82,4 +82,64 @@ public function test_void_tags_are_case_insensitive(): void $this->assertSame("
\n

Hello

", $html); } + + #[Test] + public function lex_keeps_script_content_raw(): void + { + $html = + (string) new Parser(highlighter: null, rules: [new HtmlRule()])->parse( + '', + ); + + $this->assertSame('', $html); + } + + #[Test] + public function lex_keeps_style_content_raw(): void + { + $html = + (string) new Parser(highlighter: null, rules: [new HtmlRule()])->parse( + '', + ); + + $this->assertSame('', $html); + } + + #[Test] + public function lex_keeps_pre_and_textarea_content_raw(): void + { + $parser = new Parser(highlighter: null, rules: [new HtmlRule()]); + + $this->assertSame( + '
**a** _b_
', + (string) $parser->parse('
**a** _b_
'), + ); + + $this->assertSame( + '', + (string) $parser->parse(''), + ); + } + + #[Test] + public function lex_detects_raw_text_tags_case_insensitively(): void + { + $html = + (string) new Parser(highlighter: null, rules: [new HtmlRule()])->parse( + '', + ); + + $this->assertSame('', $html); + } + + #[Test] + public function lex_still_parses_markdown_in_other_elements(): void + { + $html = + (string) new Parser(highlighter: null, rules: [new HtmlRule()])->parse( + '
**a**
', + ); + + $this->assertSame('
a
', $html); + } }