From 8eb8a131871ee2b76c7c9fe40c855972ff5c4df5 Mon Sep 17 00:00:00 2001 From: Evan Jacobs <570070+quantizor@users.noreply.github.com> Date: Wed, 20 Mar 2024 23:29:44 -0400 Subject: [PATCH] fix: handle newlines inside HTML tag brackets (#557) * refactor: trim style keys * fix: handle newlines inside HTML tag brackets Closes #540 --- .changeset/fast-months-play.md | 5 +++++ index.compiler.spec.tsx | 31 +++++++++++++++++++++++++++++++ index.tsx | 15 ++++++++------- 3 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 .changeset/fast-months-play.md diff --git a/.changeset/fast-months-play.md b/.changeset/fast-months-play.md new file mode 100644 index 00000000..037044d2 --- /dev/null +++ b/.changeset/fast-months-play.md @@ -0,0 +1,5 @@ +--- +"markdown-to-jsx": patch +--- + +Handle newlines inside of HTML tags themselves (not just nested children.) diff --git a/index.compiler.spec.tsx b/index.compiler.spec.tsx index b8c6c809..e6a83e67 100644 --- a/index.compiler.spec.tsx +++ b/index.compiler.spec.tsx @@ -3213,6 +3213,7 @@ comment -->`) `) }) + it('should not fail with lots of \\n in the middle of the text', () => { render( compiler( @@ -3362,6 +3363,36 @@ print("hello world") `) }) + + it('#540 multiline attributes are supported', () => { + render( + compiler( + `
+Item detail +debug item 1 +
` + ) + ) + + expect(root.innerHTML).toMatchInlineSnapshot(` ++ Item detail + + debug item 1 + +
+ `) + }) }) describe('horizontal rules', () => { diff --git a/index.tsx b/index.tsx index 2425a164..06974546 100644 --- a/index.tsx +++ b/index.tsx @@ -241,7 +241,7 @@ const HEADING_SETEXT_R = /^([^\n]+)\n *(=|-){3,} *(?:\n *)+\n/ * ([^ >/]+) * * 3. Ignore a space after the starting tag and capture the attribute portion of the tag (capture 2) - * ?([^>]*)\/{0}> + * ?([^>]*)> * * 4. Ensure a matching closing tag is present in the rest of the input string * (?=[\s\S]*<\/\1>) @@ -254,7 +254,7 @@ const HEADING_SETEXT_R = /^([^\n]+)\n *(=|-){3,} *(?:\n *)+\n/ * \n* */ const HTML_BLOCK_ELEMENT_R = - /^ *(?!<[a-z][^ >/]* ?\/>)<([a-z][^ >/]*) ?([^>]*)\/{0}>\n?(\s*(?:<\1[^>]*?>[\s\S]*?<\/\1>|(?!<\1)[\s\S])*?)<\/\1>\n*/i + /^ *(?!<[a-z][^ >/]* ?\/>)<([a-z][^ >/]*) ?([^>]*)>\n?(\s*(?:<\1[^>]*?>[\s\S]*?<\/\1>|(?!<\1)[\s\S])*?)<\/\1>\n*/i const HTML_CHAR_CODE_R = /&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-fA-F]{1,6});/gi @@ -716,9 +716,9 @@ function attributeValueToJSXPropValue( // snake-case to camelCase // also handles PascalCasing vendor prefixes - const camelCasedKey = key.replace(/(-[a-z])/g, substr => - substr[1].toUpperCase() - ) + const camelCasedKey = key + .trim() + .replace(/(-[a-z])/g, substr => substr[1].toUpperCase()) // key.length + 1 to skip over the colon styles[camelCasedKey] = kvPair.slice(key.length + 1).trim() @@ -1456,6 +1456,7 @@ export function compiler( order: Priority.HIGH, parse(capture, parse, state) { const [, whitespace] = capture[3].match(HTML_LEFT_TRIM_AMOUNT_R) + const trimmer = new RegExp(`^${whitespace}`, 'gm') const trimmed = capture[3].replace(trimmer, '') @@ -1470,7 +1471,7 @@ export function compiler( const ast = { attrs: attrStringToMap(capture[2]), noInnerParse: noInnerParse, - tag: noInnerParse ? tagName : capture[1], + tag: (noInnerParse ? tagName : capture[1]).trim(), } as { attrs: ReturnType