From e46a71e8a326ae0b3045cf6cde8be5fc7e988e4e Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 2 Jan 2024 16:25:58 +0100 Subject: [PATCH] fix: handle pseudo class elements with content (#10055) closes #9398 (the other things in that issue are already addressed) closes #10019 --- .changeset/nasty-lions-double.md | 5 + .changeset/sweet-pens-sniff.md | 5 + .../src/compiler/phases/1-parse/read/style.js | 6 + .../compiler/phases/2-analyze/css/Selector.js | 4 +- .../samples/css-pseudo-classes/input.svelte | 18 ++ .../samples/css-pseudo-classes/output.json | 246 ++++++++++++++++++ .../errors.json | 4 +- .../input.svelte | 3 + 8 files changed, 288 insertions(+), 3 deletions(-) create mode 100644 .changeset/nasty-lions-double.md create mode 100644 .changeset/sweet-pens-sniff.md 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/.changeset/nasty-lions-double.md b/.changeset/nasty-lions-double.md new file mode 100644 index 0000000000..404e08168d --- /dev/null +++ b/.changeset/nasty-lions-double.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve pseudo class parsing diff --git a/.changeset/sweet-pens-sniff.md b/.changeset/sweet-pens-sniff.md new file mode 100644 index 0000000000..165c1e05f7 --- /dev/null +++ b/.changeset/sweet-pens-sniff.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow pseudo classes after `:global(..)` 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 4d1777638d..600c10f33a 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/style.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/style.js @@ -227,6 +227,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/src/compiler/phases/2-analyze/css/Selector.js b/packages/svelte/src/compiler/phases/2-analyze/css/Selector.js index 2a8e4eff40..b6796ff961 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/Selector.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/Selector.js @@ -184,7 +184,9 @@ export default class Selector { selector.name === 'global' && block.selectors.length !== 1 && (i === block.selectors.length - 1 || - block.selectors.slice(i + 1).some((s) => s.type !== 'PseudoElementSelector')) + block.selectors + .slice(i + 1) + .some((s) => s.type !== 'PseudoElementSelector' && s.type !== 'PseudoClassSelector')) ) { error(selector, 'invalid-css-global-selector-list'); } 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 +} diff --git a/packages/svelte/tests/validator/samples/css-invalid-global-placement-2/errors.json b/packages/svelte/tests/validator/samples/css-invalid-global-placement-2/errors.json index 7ba32a53fb..53e24d8d81 100644 --- a/packages/svelte/tests/validator/samples/css-invalid-global-placement-2/errors.json +++ b/packages/svelte/tests/validator/samples/css-invalid-global-placement-2/errors.json @@ -3,11 +3,11 @@ "code": "invalid-css-global-placement", "message": ":global(...) can be at the start or end of a selector sequence, but not in the middle", "start": { - "line": 2, + "line": 5, "column": 6 }, "end": { - "line": 2, + "line": 5, "column": 19 } } diff --git a/packages/svelte/tests/validator/samples/css-invalid-global-placement-2/input.svelte b/packages/svelte/tests/validator/samples/css-invalid-global-placement-2/input.svelte index 75bd7b66e1..2c8e96a484 100644 --- a/packages/svelte/tests/validator/samples/css-invalid-global-placement-2/input.svelte +++ b/packages/svelte/tests/validator/samples/css-invalid-global-placement-2/input.svelte @@ -1,4 +1,7 @@