diff --git a/.changeset/three-suits-grin.md b/.changeset/three-suits-grin.md new file mode 100644 index 0000000000..7fa0154e5f --- /dev/null +++ b/.changeset/three-suits-grin.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: parse `:nth-of-type(xn+y)` correctly 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 8b1d785446..4d1777638d 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/style.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/style.js @@ -6,7 +6,8 @@ const REGEX_ATTRIBUTE_FLAGS = /^[a-zA-Z]+/; // only `i` and `s` are valid today, const REGEX_COMBINATOR_WHITESPACE = /^\s*(\+|~|>|\|\|)\s*/; const REGEX_COMBINATOR = /^(\+|~|>|\|\|)/; const REGEX_PERCENTAGE = /^\d+(\.\d+)?%/; -const REGEX_NTH_OF = /^\s*(even|odd|(-?[0-9]?n?(\s*\+\s*[0-9]+)?))(\s*(?=[,)])|\s+of\s+)/; +const REGEX_NTH_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/; @@ -277,6 +278,14 @@ function read_selector(parser, inside_pseudo_class = false) { value, flags }); + } 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)), + start, + end: parser.index + }); } else if (parser.match_regex(REGEX_COMBINATOR_WHITESPACE)) { parser.allow_whitespace(); const start = parser.index; @@ -294,13 +303,6 @@ function read_selector(parser, inside_pseudo_class = false) { start, end: parser.index }); - } else if (inside_pseudo_class && parser.match_regex(REGEX_NTH_OF)) { - children.push({ - type: 'Nth', - value: /** @type {string} */ (parser.read(REGEX_NTH_OF)), - start, - end: parser.index - }); } else { let name = read_identifier(parser); if (parser.match('|')) { diff --git a/packages/svelte/tests/parser-modern/samples/css-nth-syntax/input.svelte b/packages/svelte/tests/parser-modern/samples/css-nth-syntax/input.svelte index 5668a7799a..ce3813f3ad 100644 --- a/packages/svelte/tests/parser-modern/samples/css-nth-syntax/input.svelte +++ b/packages/svelte/tests/parser-modern/samples/css-nth-syntax/input.svelte @@ -28,6 +28,18 @@ } h1:global(nav) { background: red; + } + h1:nth-of-type(10n+1){ + background: red; + } + h1:nth-of-type(-2n+3){ + background: red; + } + h1:nth-of-type(+12){ + background: red; + } + h1:nth-of-type(+3n){ + background: red; } 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 3716981b37..87ab7c02eb 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 @@ -2,7 +2,7 @@ "css": { "type": "Style", "start": 0, - "end": 586, + "end": 806, "attributes": [], "children": [ { @@ -601,32 +601,292 @@ }, "start": 530, "end": 577 + }, + { + "type": "Rule", + "prelude": { + "type": "SelectorList", + "start": 580, + "end": 601, + "children": [ + { + "type": "Selector", + "start": 580, + "end": 601, + "children": [ + { + "type": "TypeSelector", + "name": "h1", + "start": 580, + "end": 582 + }, + { + "type": "PseudoClassSelector", + "name": "nth-of-type", + "args": { + "type": "SelectorList", + "start": 595, + "end": 600, + "children": [ + { + "type": "Selector", + "start": 595, + "end": 600, + "children": [ + { + "type": "Nth", + "value": "10n+1", + "start": 595, + "end": 600 + } + ] + } + ] + }, + "start": 582, + "end": 601 + } + ] + } + ] + }, + "block": { + "type": "Block", + "start": 601, + "end": 633, + "children": [ + { + "type": "Declaration", + "start": 611, + "end": 626, + "property": "background", + "value": "red" + } + ] + }, + "start": 580, + "end": 633 + }, + { + "type": "Rule", + "prelude": { + "type": "SelectorList", + "start": 636, + "end": 657, + "children": [ + { + "type": "Selector", + "start": 636, + "end": 657, + "children": [ + { + "type": "TypeSelector", + "name": "h1", + "start": 636, + "end": 638 + }, + { + "type": "PseudoClassSelector", + "name": "nth-of-type", + "args": { + "type": "SelectorList", + "start": 651, + "end": 656, + "children": [ + { + "type": "Selector", + "start": 651, + "end": 656, + "children": [ + { + "type": "Nth", + "value": "-2n+3", + "start": 651, + "end": 656 + } + ] + } + ] + }, + "start": 638, + "end": 657 + } + ] + } + ] + }, + "block": { + "type": "Block", + "start": 657, + "end": 689, + "children": [ + { + "type": "Declaration", + "start": 667, + "end": 682, + "property": "background", + "value": "red" + } + ] + }, + "start": 636, + "end": 689 + }, + { + "type": "Rule", + "prelude": { + "type": "SelectorList", + "start": 692, + "end": 711, + "children": [ + { + "type": "Selector", + "start": 692, + "end": 711, + "children": [ + { + "type": "TypeSelector", + "name": "h1", + "start": 692, + "end": 694 + }, + { + "type": "PseudoClassSelector", + "name": "nth-of-type", + "args": { + "type": "SelectorList", + "start": 707, + "end": 710, + "children": [ + { + "type": "Selector", + "start": 707, + "end": 710, + "children": [ + { + "type": "Nth", + "value": "+12", + "start": 707, + "end": 710 + } + ] + } + ] + }, + "start": 694, + "end": 711 + } + ] + } + ] + }, + "block": { + "type": "Block", + "start": 711, + "end": 743, + "children": [ + { + "type": "Declaration", + "start": 721, + "end": 736, + "property": "background", + "value": "red" + } + ] + }, + "start": 692, + "end": 743 + }, + { + "type": "Rule", + "prelude": { + "type": "SelectorList", + "start": 746, + "end": 765, + "children": [ + { + "type": "Selector", + "start": 746, + "end": 765, + "children": [ + { + "type": "TypeSelector", + "name": "h1", + "start": 746, + "end": 748 + }, + { + "type": "PseudoClassSelector", + "name": "nth-of-type", + "args": { + "type": "SelectorList", + "start": 761, + "end": 764, + "children": [ + { + "type": "Selector", + "start": 761, + "end": 764, + "children": [ + { + "type": "Nth", + "value": "+3n", + "start": 761, + "end": 764 + } + ] + } + ] + }, + "start": 748, + "end": 765 + } + ] + } + ] + }, + "block": { + "type": "Block", + "start": 765, + "end": 797, + "children": [ + { + "type": "Declaration", + "start": 775, + "end": 790, + "property": "background", + "value": "red" + } + ] + }, + "start": 746, + "end": 797 } ], "content": { "start": 7, - "end": 578, - "styles": "\n /* test that all these are parsed correctly */\n\th1:nth-of-type(2n+1){\n background: red;\n }\n h1:nth-child(-n + 3 of li.important) {\n background: red;\n }\n h1:nth-child(1) {\n background: red;\n }\n h1:nth-child(p) {\n background: red;\n }\n h1:nth-child(n+7) {\n background: red;\n }\n h1:nth-child(even) {\n background: red;\n }\n h1:nth-child(odd) {\n background: red;\n }\n h1:nth-child(\n n\n ) {\n background: red;\n }\n h1:global(nav) {\n background: red;\n }\n" + "end": 798, + "styles": "\n /* test that all these are parsed correctly */\n\th1:nth-of-type(2n+1){\n background: red;\n }\n h1:nth-child(-n + 3 of li.important) {\n background: red;\n }\n h1:nth-child(1) {\n background: red;\n }\n h1:nth-child(p) {\n background: red;\n }\n h1:nth-child(n+7) {\n background: red;\n }\n h1:nth-child(even) {\n background: red;\n }\n h1:nth-child(odd) {\n background: red;\n }\n h1:nth-child(\n n\n ) {\n background: red;\n }\n h1:global(nav) {\n background: red;\n }\n\t\th1:nth-of-type(10n+1){\n background: red;\n }\n\t\th1:nth-of-type(-2n+3){\n background: red;\n }\n\t\th1:nth-of-type(+12){\n background: red;\n }\n\t\th1:nth-of-type(+3n){\n background: red;\n }\n" } }, "js": [], - "start": 588, - "end": 600, + "start": 808, + "end": 820, "type": "Root", "fragment": { "type": "Fragment", "nodes": [ { "type": "Text", - "start": 586, - "end": 588, + "start": 806, + "end": 808, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 588, - "end": 600, + "start": 808, + "end": 820, "name": "h1", "attributes": [], "fragment": { @@ -634,8 +894,8 @@ "nodes": [ { "type": "Text", - "start": 592, - "end": 595, + "start": 812, + "end": 815, "raw": "Foo", "data": "Foo" }