From a58d93f8bf61b56cdc7c11c2d730d0cb1c5be529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rog=C3=A9rio=20Luiz=20Aques=20de=20Amorim?= Date: Thu, 25 Jan 2024 22:26:39 -0300 Subject: [PATCH] fix: parser error when using semicolon inside quotes in style (#10221) * fix error when using semicolon inside quotes in style * refactor to include { like in the original code * simplified version of regex Co-authored-by: navorite * add changeset * add changeset * add test * Update .changeset/seven-hornets-smile.md Co-authored-by: navorite * undo demo.css change * fix support-font-face test not passing * add double quotes * beef up test * robustify parsing * Update .changeset/seven-hornets-smile.md --------- Co-authored-by: navorite Co-authored-by: Rogerio Luiz Aques de Amorim Co-authored-by: Rich Harris Co-authored-by: Rich Harris --- .changeset/seven-hornets-smile.md | 5 + .../src/compiler/phases/1-parse/read/style.js | 9 +- .../semicolon-inside-quotes/input.svelte | 10 ++ .../semicolon-inside-quotes/output.json | 107 ++++++++++++++++++ 4 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 .changeset/seven-hornets-smile.md create mode 100644 packages/svelte/tests/parser-modern/samples/semicolon-inside-quotes/input.svelte create mode 100644 packages/svelte/tests/parser-modern/samples/semicolon-inside-quotes/output.json diff --git a/.changeset/seven-hornets-smile.md b/.changeset/seven-hornets-smile.md new file mode 100644 index 0000000000..7ae0106acb --- /dev/null +++ b/.changeset/seven-hornets-smile.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: correctly parse at-rules containing special characters in strings diff --git a/packages/svelte/src/compiler/phases/1-parse/read/style.js b/packages/svelte/src/compiler/phases/1-parse/read/style.js index 545542d80f..ff9f9a8fec 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/style.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/style.js @@ -9,7 +9,6 @@ const REGEX_PERCENTAGE = /^\d+(\.\d+)?%/; const REGEX_NTH_OF = /^(even|odd|\+?(\d+|\d*n(\s*[+-]\s*\d+)?)|-\d*n(\s*\+\s*\d+))((?=\s*[,)])|\s+of\s+)/; const REGEX_WHITESPACE_OR_COLON = /[\s:]/; -const REGEX_BRACE_OR_SEMICOLON = /[{;]/; const REGEX_LEADING_HYPHEN_OR_DIGIT = /-?\d/; const REGEX_VALID_IDENTIFIER_CHAR = /[a-zA-Z0-9_-]/; const REGEX_COMMENT_CLOSE = /\*\//; @@ -79,7 +78,7 @@ function read_at_rule(parser) { const name = read_identifier(parser); - const prelude = parser.read_until(REGEX_BRACE_OR_SEMICOLON).trim(); + const prelude = read_value(parser); /** @type {import('#compiler').Css.Block | null} */ let block = null; @@ -398,7 +397,7 @@ function read_declaration(parser) { parser.eat(':'); parser.allow_whitespace(); - const value = read_declaration_value(parser); + const value = read_value(parser); const end = parser.index; @@ -419,7 +418,7 @@ function read_declaration(parser) { * @param {import('../index.js').Parser} parser * @returns {string} */ -function read_declaration_value(parser) { +function read_value(parser) { let value = ''; let escaped = false; let in_url = false; @@ -443,7 +442,7 @@ function read_declaration_value(parser) { quote_mark = char; } else if (char === '(' && value.slice(-3) === 'url') { in_url = true; - } else if ((char === ';' || char === '}') && !in_url && !quote_mark) { + } else if ((char === ';' || char === '{' || char === '}') && !in_url && !quote_mark) { return value.trim(); } diff --git a/packages/svelte/tests/parser-modern/samples/semicolon-inside-quotes/input.svelte b/packages/svelte/tests/parser-modern/samples/semicolon-inside-quotes/input.svelte new file mode 100644 index 0000000000..8473391f94 --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/semicolon-inside-quotes/input.svelte @@ -0,0 +1,10 @@ +

+ Semicolon inside quotes +

+ diff --git a/packages/svelte/tests/parser-modern/samples/semicolon-inside-quotes/output.json b/packages/svelte/tests/parser-modern/samples/semicolon-inside-quotes/output.json new file mode 100644 index 0000000000..c6fbb599c7 --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/semicolon-inside-quotes/output.json @@ -0,0 +1,107 @@ +{ + "css": { + "type": "Style", + "start": 36, + "end": 205, + "attributes": [], + "children": [ + { + "type": "Atrule", + "start": 45, + "end": 135, + "name": "import", + "prelude": "url(\"https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap\")", + "block": null + }, + { + "type": "Rule", + "prelude": { + "type": "SelectorList", + "start": 137, + "end": 139, + "children": [ + { + "type": "Selector", + "start": 137, + "end": 139, + "children": [ + { + "type": "TypeSelector", + "name": "h1", + "start": 137, + "end": 139 + } + ] + } + ] + }, + "block": { + "type": "Block", + "start": 140, + "end": 196, + "children": [ + { + "type": "Declaration", + "start": 144, + "end": 161, + "property": "font-weight", + "value": "bold" + }, + { + "type": "Declaration", + "start": 165, + "end": 192, + "property": "background", + "value": "url(\"whatever\")" + } + ] + }, + "start": 137, + "end": 196 + } + ], + "content": { + "start": 43, + "end": 197, + "styles": "\n\t@import url(\"https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap\");\n\th1 {\n\t\tfont-weight: bold;\n\t\tbackground: url(\"whatever\");\n\t}\n" + } + }, + "js": [], + "start": 0, + "end": 35, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "RegularElement", + "start": 0, + "end": 35, + "name": "h1", + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 4, + "end": 30, + "raw": "\n\tSemicolon inside quotes\n", + "data": "\n\tSemicolon inside quotes\n" + } + ], + "transparent": true + } + }, + { + "type": "Text", + "start": 35, + "end": 36, + "raw": "\n", + "data": "\n" + } + ], + "transparent": false + }, + "options": null +}