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 @@