diff --git a/.changeset/sweet-mangos-beg.md b/.changeset/sweet-mangos-beg.md
new file mode 100644
index 0000000000..3a0adfc03c
--- /dev/null
+++ b/.changeset/sweet-mangos-beg.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: handle css nth-selector syntax
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 5523e13f95..9f59e8205e 100644
--- a/packages/svelte/src/compiler/phases/1-parse/read/style.js
+++ b/packages/svelte/src/compiler/phases/1-parse/read/style.js
@@ -6,6 +6,7 @@ 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 = /^(even|odd|(-?[0-9]?n?(\s*\+\s*[0-9]+)?))(\s+of\s+)?/;
const REGEX_WHITESPACE_OR_COLON = /[\s:]/;
const REGEX_BRACE_OR_SEMICOLON = /[{;]/;
const REGEX_LEADING_HYPHEN_OR_DIGIT = /-?\d/;
@@ -293,6 +294,13 @@ function read_selector(parser, inside_pseudo_class = false) {
start,
end: parser.index
});
+ } else if (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/src/compiler/types/css.d.ts b/packages/svelte/src/compiler/types/css.d.ts
index 7d936207ac..49a3fc5143 100644
--- a/packages/svelte/src/compiler/types/css.d.ts
+++ b/packages/svelte/src/compiler/types/css.d.ts
@@ -67,6 +67,11 @@ export interface Percentage extends BaseNode {
value: string;
}
+export interface Nth extends BaseNode {
+ type: 'Nth';
+ value: string;
+}
+
export type SimpleSelector =
| TypeSelector
| IdSelector
@@ -74,7 +79,8 @@ export type SimpleSelector =
| AttributeSelector
| PseudoElementSelector
| PseudoClassSelector
- | Percentage;
+ | Percentage
+ | Nth;
export interface Combinator extends BaseNode {
type: 'Combinator';
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
new file mode 100644
index 0000000000..b8e2d40864
--- /dev/null
+++ b/packages/svelte/tests/parser-modern/samples/css-nth-syntax/input.svelte
@@ -0,0 +1,26 @@
+
+
+
Broken
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
new file mode 100644
index 0000000000..1cea48b17c
--- /dev/null
+++ b/packages/svelte/tests/parser-modern/samples/css-nth-syntax/output.json
@@ -0,0 +1,520 @@
+{
+ "css": {
+ "type": "Style",
+ "start": 0,
+ "end": 467,
+ "attributes": [],
+ "children": [
+ {
+ "type": "Rule",
+ "prelude": {
+ "type": "SelectorList",
+ "start": 60,
+ "end": 80,
+ "children": [
+ {
+ "type": "Selector",
+ "start": 60,
+ "end": 80,
+ "children": [
+ {
+ "type": "TypeSelector",
+ "name": "h1",
+ "start": 60,
+ "end": 62
+ },
+ {
+ "type": "PseudoClassSelector",
+ "name": "nth-of-type",
+ "args": {
+ "type": "SelectorList",
+ "start": 75,
+ "end": 79,
+ "children": [
+ {
+ "type": "Selector",
+ "start": 75,
+ "end": 79,
+ "children": [
+ {
+ "type": "Nth",
+ "value": "2n+1",
+ "start": 75,
+ "end": 79
+ }
+ ]
+ }
+ ]
+ },
+ "start": 62,
+ "end": 80
+ }
+ ]
+ }
+ ]
+ },
+ "block": {
+ "type": "Block",
+ "start": 80,
+ "end": 112,
+ "children": [
+ {
+ "type": "Declaration",
+ "start": 90,
+ "end": 105,
+ "property": "background",
+ "value": "red"
+ }
+ ]
+ },
+ "start": 60,
+ "end": 112
+ },
+ {
+ "type": "Rule",
+ "prelude": {
+ "type": "SelectorList",
+ "start": 117,
+ "end": 153,
+ "children": [
+ {
+ "type": "Selector",
+ "start": 117,
+ "end": 153,
+ "children": [
+ {
+ "type": "TypeSelector",
+ "name": "h1",
+ "start": 117,
+ "end": 119
+ },
+ {
+ "type": "PseudoClassSelector",
+ "name": "nth-child",
+ "args": {
+ "type": "SelectorList",
+ "start": 130,
+ "end": 152,
+ "children": [
+ {
+ "type": "Selector",
+ "start": 130,
+ "end": 152,
+ "children": [
+ {
+ "type": "Nth",
+ "value": "-n + 3 of ",
+ "start": 130,
+ "end": 140
+ },
+ {
+ "type": "TypeSelector",
+ "name": "li",
+ "start": 140,
+ "end": 142
+ },
+ {
+ "type": "ClassSelector",
+ "name": "important",
+ "start": 142,
+ "end": 152
+ }
+ ]
+ }
+ ]
+ },
+ "start": 119,
+ "end": 153
+ }
+ ]
+ }
+ ]
+ },
+ "block": {
+ "type": "Block",
+ "start": 154,
+ "end": 186,
+ "children": [
+ {
+ "type": "Declaration",
+ "start": 164,
+ "end": 179,
+ "property": "background",
+ "value": "red"
+ }
+ ]
+ },
+ "start": 117,
+ "end": 186
+ },
+ {
+ "type": "Rule",
+ "prelude": {
+ "type": "SelectorList",
+ "start": 191,
+ "end": 206,
+ "children": [
+ {
+ "type": "Selector",
+ "start": 191,
+ "end": 206,
+ "children": [
+ {
+ "type": "TypeSelector",
+ "name": "h1",
+ "start": 191,
+ "end": 193
+ },
+ {
+ "type": "PseudoClassSelector",
+ "name": "nth-child",
+ "args": {
+ "type": "SelectorList",
+ "start": 204,
+ "end": 205,
+ "children": [
+ {
+ "type": "Selector",
+ "start": 204,
+ "end": 205,
+ "children": [
+ {
+ "type": "Nth",
+ "value": "1",
+ "start": 204,
+ "end": 205
+ }
+ ]
+ }
+ ]
+ },
+ "start": 193,
+ "end": 206
+ }
+ ]
+ }
+ ]
+ },
+ "block": {
+ "type": "Block",
+ "start": 207,
+ "end": 239,
+ "children": [
+ {
+ "type": "Declaration",
+ "start": 217,
+ "end": 232,
+ "property": "background",
+ "value": "red"
+ }
+ ]
+ },
+ "start": 191,
+ "end": 239
+ },
+ {
+ "type": "Rule",
+ "prelude": {
+ "type": "SelectorList",
+ "start": 244,
+ "end": 259,
+ "children": [
+ {
+ "type": "Selector",
+ "start": 244,
+ "end": 259,
+ "children": [
+ {
+ "type": "TypeSelector",
+ "name": "h1",
+ "start": 244,
+ "end": 246
+ },
+ {
+ "type": "PseudoClassSelector",
+ "name": "nth-child",
+ "args": {
+ "type": "SelectorList",
+ "start": 257,
+ "end": 258,
+ "children": [
+ {
+ "type": "Selector",
+ "start": 257,
+ "end": 258,
+ "children": [
+ {
+ "type": "TypeSelector",
+ "name": "p",
+ "start": 257,
+ "end": 258
+ }
+ ]
+ }
+ ]
+ },
+ "start": 246,
+ "end": 259
+ }
+ ]
+ }
+ ]
+ },
+ "block": {
+ "type": "Block",
+ "start": 260,
+ "end": 292,
+ "children": [
+ {
+ "type": "Declaration",
+ "start": 270,
+ "end": 285,
+ "property": "background",
+ "value": "red"
+ }
+ ]
+ },
+ "start": 244,
+ "end": 292
+ },
+ {
+ "type": "Rule",
+ "prelude": {
+ "type": "SelectorList",
+ "start": 297,
+ "end": 314,
+ "children": [
+ {
+ "type": "Selector",
+ "start": 297,
+ "end": 314,
+ "children": [
+ {
+ "type": "TypeSelector",
+ "name": "h1",
+ "start": 297,
+ "end": 299
+ },
+ {
+ "type": "PseudoClassSelector",
+ "name": "nth-child",
+ "args": {
+ "type": "SelectorList",
+ "start": 310,
+ "end": 313,
+ "children": [
+ {
+ "type": "Selector",
+ "start": 310,
+ "end": 313,
+ "children": [
+ {
+ "type": "Nth",
+ "value": "n+7",
+ "start": 310,
+ "end": 313
+ }
+ ]
+ }
+ ]
+ },
+ "start": 299,
+ "end": 314
+ }
+ ]
+ }
+ ]
+ },
+ "block": {
+ "type": "Block",
+ "start": 315,
+ "end": 347,
+ "children": [
+ {
+ "type": "Declaration",
+ "start": 325,
+ "end": 340,
+ "property": "background",
+ "value": "red"
+ }
+ ]
+ },
+ "start": 297,
+ "end": 347
+ },
+ {
+ "type": "Rule",
+ "prelude": {
+ "type": "SelectorList",
+ "start": 352,
+ "end": 370,
+ "children": [
+ {
+ "type": "Selector",
+ "start": 352,
+ "end": 370,
+ "children": [
+ {
+ "type": "TypeSelector",
+ "name": "h1",
+ "start": 352,
+ "end": 354
+ },
+ {
+ "type": "PseudoClassSelector",
+ "name": "nth-child",
+ "args": {
+ "type": "SelectorList",
+ "start": 365,
+ "end": 369,
+ "children": [
+ {
+ "type": "Selector",
+ "start": 365,
+ "end": 369,
+ "children": [
+ {
+ "type": "Nth",
+ "value": "even",
+ "start": 365,
+ "end": 369
+ }
+ ]
+ }
+ ]
+ },
+ "start": 354,
+ "end": 370
+ }
+ ]
+ }
+ ]
+ },
+ "block": {
+ "type": "Block",
+ "start": 371,
+ "end": 403,
+ "children": [
+ {
+ "type": "Declaration",
+ "start": 381,
+ "end": 396,
+ "property": "background",
+ "value": "red"
+ }
+ ]
+ },
+ "start": 352,
+ "end": 403
+ },
+ {
+ "type": "Rule",
+ "prelude": {
+ "type": "SelectorList",
+ "start": 408,
+ "end": 425,
+ "children": [
+ {
+ "type": "Selector",
+ "start": 408,
+ "end": 425,
+ "children": [
+ {
+ "type": "TypeSelector",
+ "name": "h1",
+ "start": 408,
+ "end": 410
+ },
+ {
+ "type": "PseudoClassSelector",
+ "name": "nth-child",
+ "args": {
+ "type": "SelectorList",
+ "start": 421,
+ "end": 424,
+ "children": [
+ {
+ "type": "Selector",
+ "start": 421,
+ "end": 424,
+ "children": [
+ {
+ "type": "Nth",
+ "value": "odd",
+ "start": 421,
+ "end": 424
+ }
+ ]
+ }
+ ]
+ },
+ "start": 410,
+ "end": 425
+ }
+ ]
+ }
+ ]
+ },
+ "block": {
+ "type": "Block",
+ "start": 426,
+ "end": 458,
+ "children": [
+ {
+ "type": "Declaration",
+ "start": 436,
+ "end": 451,
+ "property": "background",
+ "value": "red"
+ }
+ ]
+ },
+ "start": 408,
+ "end": 458
+ }
+ ],
+ "content": {
+ "start": 7,
+ "end": 459,
+ "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"
+ }
+ },
+ "js": [],
+ "start": 469,
+ "end": 484,
+ "type": "Root",
+ "fragment": {
+ "type": "Fragment",
+ "nodes": [
+ {
+ "type": "Text",
+ "start": 467,
+ "end": 469,
+ "raw": "\n\n",
+ "data": "\n\n"
+ },
+ {
+ "type": "RegularElement",
+ "start": 469,
+ "end": 484,
+ "name": "h1",
+ "attributes": [],
+ "fragment": {
+ "type": "Fragment",
+ "nodes": [
+ {
+ "type": "Text",
+ "start": 473,
+ "end": 479,
+ "raw": "Broken",
+ "data": "Broken"
+ }
+ ],
+ "transparent": true
+ }
+ }
+ ],
+ "transparent": false
+ },
+ "options": null
+}