From 5bb2bbfa8ff54d77c1c31d21f794d885992b7996 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 2 Jan 2024 15:27:30 +0100 Subject: [PATCH] fix: handle pseudo class elements with content closes #9398 (the other things in that issue are already addressed) --- .../src/compiler/phases/1-parse/read/style.js | 6 + .../samples/css-pseudo-classes/input.svelte | 18 ++ .../samples/css-pseudo-classes/output.json | 246 ++++++++++++++++++ 3 files changed, 270 insertions(+) create mode 100644 packages/svelte/tests/parser-modern/samples/css-pseudo-classes/input.svelte create mode 100644 packages/svelte/tests/parser-modern/samples/css-pseudo-classes/output.json 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..2abe1c1b59 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/style.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/style.js @@ -226,6 +226,12 @@ function read_selector(parser, inside_pseudo_class = false) { start, end: parser.index }); + // We read the inner selectors of a pseudo element to ensure it parses correctly, + // but we don't do anything with the result. + if (parser.eat('(')) { + read_selector_list(parser, true); + parser.eat(')', true); + } } else if (parser.eat(':')) { const name = read_identifier(parser); 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 new file mode 100644 index 0000000000..6602f9c044 --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/css-pseudo-classes/input.svelte @@ -0,0 +1,18 @@ + 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 new file mode 100644 index 0000000000..f502060e13 --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/css-pseudo-classes/output.json @@ -0,0 +1,246 @@ +{ + "css": { + "type": "Style", + "start": 0, + "end": 313, + "attributes": [], + "children": [ + { + "type": "Rule", + "prelude": { + "type": "SelectorList", + "start": 60, + "end": 86, + "children": [ + { + "type": "Selector", + "start": 60, + "end": 86, + "children": [ + { + "type": "PseudoElementSelector", + "name": "view-transition-old", + "start": 60, + "end": 81 + } + ] + } + ] + }, + "block": { + "type": "Block", + "start": 88, + "end": 109, + "children": [ + { + "type": "Declaration", + "start": 92, + "end": 102, + "property": "color", + "value": "red" + } + ] + }, + "start": 60, + "end": 109 + }, + { + "type": "Rule", + "prelude": { + "type": "SelectorList", + "start": 111, + "end": 146, + "children": [ + { + "type": "Selector", + "start": 111, + "end": 146, + "children": [ + { + "type": "PseudoClassSelector", + "name": "global", + "args": { + "type": "SelectorList", + "start": 119, + "end": 145, + "children": [ + { + "type": "Selector", + "start": 119, + "end": 145, + "children": [ + { + "type": "PseudoElementSelector", + "name": "view-transition-old", + "start": 119, + "end": 140 + } + ] + } + ] + }, + "start": 111, + "end": 146 + } + ] + } + ] + }, + "block": { + "type": "Block", + "start": 148, + "end": 169, + "children": [ + { + "type": "Declaration", + "start": 152, + "end": 162, + "property": "color", + "value": "red" + } + ] + }, + "start": 111, + "end": 169 + }, + { + "type": "Rule", + "prelude": { + "type": "SelectorList", + "start": 171, + "end": 199, + "children": [ + { + "type": "Selector", + "start": 171, + "end": 199, + "children": [ + { + "type": "PseudoElementSelector", + "name": "highlight", + "start": 171, + "end": 182 + } + ] + } + ] + }, + "block": { + "type": "Block", + "start": 200, + "end": 218, + "children": [ + { + "type": "Declaration", + "start": 204, + "end": 214, + "property": "color", + "value": "red" + } + ] + }, + "start": 171, + "end": 218 + }, + { + "type": "Rule", + "prelude": { + "type": "SelectorList", + "start": 220, + "end": 245, + "children": [ + { + "type": "Selector", + "start": 220, + "end": 245, + "children": [ + { + "type": "TypeSelector", + "name": "custom-element", + "start": 220, + "end": 234 + }, + { + "type": "PseudoElementSelector", + "name": "part", + "start": 234, + "end": 240 + } + ] + } + ] + }, + "block": { + "type": "Block", + "start": 246, + "end": 264, + "children": [ + { + "type": "Declaration", + "start": 250, + "end": 260, + "property": "color", + "value": "red" + } + ] + }, + "start": 220, + "end": 264 + }, + { + "type": "Rule", + "prelude": { + "type": "SelectorList", + "start": 266, + "end": 285, + "children": [ + { + "type": "Selector", + "start": 266, + "end": 285, + "children": [ + { + "type": "PseudoElementSelector", + "name": "slotted", + "start": 266, + "end": 275 + } + ] + } + ] + }, + "block": { + "type": "Block", + "start": 286, + "end": 304, + "children": [ + { + "type": "Declaration", + "start": 290, + "end": 300, + "property": "color", + "value": "red" + } + ] + }, + "start": 266, + "end": 304 + } + ], + "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" + } + }, + "js": [], + "start": null, + "end": null, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [], + "transparent": false + }, + "options": null +}