diff --git a/.changeset/dry-eggs-retire.md b/.changeset/dry-eggs-retire.md new file mode 100644 index 0000000000..1e8bb95d75 --- /dev/null +++ b/.changeset/dry-eggs-retire.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve style parser whitespace handling 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 600c10f33a..545542d80f 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/style.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/style.js @@ -7,7 +7,7 @@ const REGEX_COMBINATOR_WHITESPACE = /^\s*(\+|~|>|\|\|)\s*/; const REGEX_COMBINATOR = /^(\+|~|>|\|\|)/; const REGEX_PERCENTAGE = /^\d+(\.\d+)?%/; const REGEX_NTH_OF = - /^\s*(even|odd|\+?(\d+|\d*n(\s*[+-]\s*\d+)?)|-\d*n(\s*\+\s*\d+))(\s*(?=[,)])|\s+of\s+)/; + /^(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/; @@ -153,6 +153,8 @@ function read_selector_list(parser, inside_pseudo_class = false) { /** @type {import('#compiler').Css.Selector[]} */ const children = []; + allow_comment_or_whitespace(parser); + const start = parser.index; while (parser.index < parser.template.length) { @@ -286,9 +288,10 @@ function read_selector(parser, inside_pseudo_class = false) { }); } else if (inside_pseudo_class && parser.match_regex(REGEX_NTH_OF)) { // nth of matcher must come before combinator matcher to prevent collision else the '+' in '+2n-1' would be parsed as a combinator + children.push({ type: 'Nth', - value: /** @type {string} */ (parser.read(REGEX_NTH_OF)), + value: /**@type {string} */ (parser.read(REGEX_NTH_OF)), start, end: parser.index }); diff --git a/packages/svelte/tests/parser-modern/samples/css-nth-syntax/output.json b/packages/svelte/tests/parser-modern/samples/css-nth-syntax/output.json index 87ab7c02eb..9e13a7cb86 100644 --- a/packages/svelte/tests/parser-modern/samples/css-nth-syntax/output.json +++ b/packages/svelte/tests/parser-modern/samples/css-nth-syntax/output.json @@ -495,19 +495,19 @@ "name": "nth-child", "args": { "type": "SelectorList", - "start": 476, - "end": 491, + "start": 485, + "end": 486, "children": [ { "type": "Selector", - "start": 476, - "end": 491, + "start": 485, + "end": 486, "children": [ { "type": "Nth", - "value": "\n n\n ", - "start": 476, - "end": 491 + "value": "n", + "start": 485, + "end": 486 } ] } diff --git a/packages/svelte/tests/parser-modern/samples/css-pseudo-classes/input.svelte b/packages/svelte/tests/parser-modern/samples/css-pseudo-classes/input.svelte index 6602f9c044..d8ed0f9e36 100644 --- a/packages/svelte/tests/parser-modern/samples/css-pseudo-classes/input.svelte +++ b/packages/svelte/tests/parser-modern/samples/css-pseudo-classes/input.svelte @@ -15,4 +15,10 @@ ::slotted(.content) { color: red; } + :is( /*button*/ + button, /*p after h1*/ + h1 + p + ){ + color: red; + } diff --git a/packages/svelte/tests/parser-modern/samples/css-pseudo-classes/output.json b/packages/svelte/tests/parser-modern/samples/css-pseudo-classes/output.json index f502060e13..1431b123a4 100644 --- a/packages/svelte/tests/parser-modern/samples/css-pseudo-classes/output.json +++ b/packages/svelte/tests/parser-modern/samples/css-pseudo-classes/output.json @@ -2,7 +2,7 @@ "css": { "type": "Style", "start": 0, - "end": 313, + "end": 386, "attributes": [], "children": [ { @@ -225,12 +225,96 @@ }, "start": 266, "end": 304 + }, + { + "type": "Rule", + "prelude": { + "type": "SelectorList", + "start": 306, + "end": 359, + "children": [ + { + "type": "Selector", + "start": 306, + "end": 359, + "children": [ + { + "type": "PseudoClassSelector", + "name": "is", + "args": { + "type": "SelectorList", + "start": 324, + "end": 355, + "children": [ + { + "type": "Selector", + "start": 324, + "end": 330, + "children": [ + { + "type": "TypeSelector", + "name": "button", + "start": 324, + "end": 330 + } + ] + }, + { + "type": "Selector", + "start": 349, + "end": 355, + "children": [ + { + "type": "TypeSelector", + "name": "h1", + "start": 349, + "end": 351 + }, + { + "type": "Combinator", + "name": "+", + "start": 352, + "end": 353 + }, + { + "type": "TypeSelector", + "name": "p", + "start": 354, + "end": 355 + } + ] + } + ] + }, + "start": 306, + "end": 359 + } + ] + } + ] + }, + "block": { + "type": "Block", + "start": 359, + "end": 377, + "children": [ + { + "type": "Declaration", + "start": 363, + "end": 373, + "property": "color", + "value": "red" + } + ] + }, + "start": 306, + "end": 377 } ], "content": { "start": 7, - "end": 305, - "styles": "\n /* test that all these are parsed correctly */\n\t::view-transition-old(x-y) {\n\t\tcolor: red;\n }\n\t:global(::view-transition-old(x-y)) {\n\t\tcolor: red;\n }\n\t::highlight(rainbow-color-1) {\n\t\tcolor: red;\n\t}\n\tcustom-element::part(foo) {\n\t\tcolor: red;\n\t}\n\t::slotted(.content) {\n\t\tcolor: red;\n\t}\n" + "end": 378, + "styles": "\n /* test that all these are parsed correctly */\n\t::view-transition-old(x-y) {\n\t\tcolor: red;\n }\n\t:global(::view-transition-old(x-y)) {\n\t\tcolor: red;\n }\n\t::highlight(rainbow-color-1) {\n\t\tcolor: red;\n\t}\n\tcustom-element::part(foo) {\n\t\tcolor: red;\n\t}\n\t::slotted(.content) {\n\t\tcolor: red;\n\t}\n\t:is( /*button*/\n\t\tbutton, /*p after h1*/\n\t\th1 + p\n\t\t){\n\t\tcolor: red;\n\t}\n" } }, "js": [],