fix: refine css `:global()` selector checks in a compound selector (#11142)

pull/11201/head
Tan Li Hau 7 months ago committed by GitHub
parent e7301af1e5
commit 3d49731b1b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: refine css `:global()` selector checks in a compound selector

@ -108,6 +108,8 @@ const css = {
'invalid-css-global-selector': () => `:global(...) must contain exactly one selector`, 'invalid-css-global-selector': () => `:global(...) must contain exactly one selector`,
'invalid-css-global-selector-list': () => 'invalid-css-global-selector-list': () =>
`:global(...) must not contain type or universal selectors when used in a compound selector`, `:global(...) must not contain type or universal selectors when used in a compound selector`,
'invalid-css-type-selector-placement': () =>
`:global(...) must not be followed with a type selector`,
'invalid-css-selector': () => `Invalid selector`, 'invalid-css-selector': () => `Invalid selector`,
'invalid-css-identifier': () => 'Expected a valid CSS identifier', 'invalid-css-identifier': () => 'Expected a valid CSS identifier',
'invalid-nesting-selector': () => `Nesting selectors can only be used inside a rule`, 'invalid-nesting-selector': () => `Nesting selectors can only be used inside a rule`,

@ -99,41 +99,31 @@ const validation_visitors = {
} }
} }
// ensure `:global(...)`contains a single selector // ensure `:global(...)` do not lead to invalid css after `:global()` is removed
// (standalone :global() with multiple selectors is OK)
if (node.children.length > 1 || node.children[0].selectors.length > 1) {
for (const relative_selector of node.children) {
for (const selector of relative_selector.selectors) {
if (
selector.type === 'PseudoClassSelector' &&
selector.name === 'global' &&
selector.args !== null &&
selector.args.children.length > 1
) {
error(selector, 'invalid-css-global-selector');
}
}
}
}
// ensure `:global(...)` is not part of a larger compound selector
for (const relative_selector of node.children) { for (const relative_selector of node.children) {
for (let i = 0; i < relative_selector.selectors.length; i++) { for (let i = 0; i < relative_selector.selectors.length; i++) {
const selector = relative_selector.selectors[i]; const selector = relative_selector.selectors[i];
if (selector.type === 'PseudoClassSelector' && selector.name === 'global') { if (selector.type === 'PseudoClassSelector' && selector.name === 'global') {
const child = selector.args?.children[0].children[0]; const child = selector.args?.children[0].children[0];
// ensure `:global(element)` to be at the first position in a compound selector
if (child?.selectors[0].type === 'TypeSelector' && i !== 0) {
error(selector, 'invalid-css-global-selector-list');
}
// ensure `:global(.class)` is not followed by a type selector, eg: `:global(.class)element`
if (relative_selector.selectors[i + 1]?.type === 'TypeSelector') {
error(relative_selector.selectors[i + 1], 'invalid-css-type-selector-placement');
}
// ensure `:global(...)`contains a single selector
// (standalone :global() with multiple selectors is OK)
if ( if (
child?.selectors[0].type === 'TypeSelector' && selector.args !== null &&
!/[.:#]/.test(child.selectors[0].name[0]) && selector.args.children.length > 1 &&
(i !== 0 || (node.children.length > 1 || relative_selector.selectors.length > 1)
relative_selector.selectors
.slice(1)
.some(
(s) => s.type !== 'PseudoElementSelector' && s.type !== 'PseudoClassSelector'
))
) { ) {
error(selector, 'invalid-css-global-selector-list'); error(selector, 'invalid-css-global-selector');
} }
} }
} }

@ -230,7 +230,7 @@ const visitors = {
context.state.specificity.bumped = true; context.state.specificity.bumped = true;
// TODO err... can this happen? // for any :global() at the middle of compound selector
for (const selector of relative_selector.selectors) { for (const selector of relative_selector.selectors) {
if (selector.type === 'PseudoClassSelector' && selector.name === 'global') { if (selector.type === 'PseudoClassSelector' && selector.name === 'global') {
remove_global_pseudo_class(selector); remove_global_pseudo_class(selector);

@ -0,0 +1,14 @@
[
{
"code": "invalid-css-global-selector-list",
"message": ":global(...) must not contain type or universal selectors when used in a compound selector",
"start": {
"line": 20,
"column": 6
},
"end": {
"line": 20,
"column": 17
}
}
]

@ -0,0 +1,27 @@
<style>
::foo:global([data-state='checked']) {
color: red;
}
::foo:global(.foo) {
color: red;
}
::foo:global(#foo) {
color: red;
}
::foo:global(::foo) {
color: red;
}
::foo:global(:foo) {
color: red;
}
:global(h1) {
color: red;
}
::foo:global(h1) {
color: red;
}
</style>
<div>
<h1>hello world</h1>
</div>

@ -0,0 +1,14 @@
[
{
"code": "invalid-css-type-selector-placement",
"message": ":global(...) must not be followed with a type selector",
"start": {
"line": 17,
"column": 14
},
"end": {
"line": 17,
"column": 16
}
}
]

@ -0,0 +1,24 @@
<style>
:global(.foo):foo {
color: red;
}
:global(.foo)::foo {
color: red;
}
:global(.foo).bar {
color: red;
}
:global(.foo)#baz {
color: red;
}
:global(.foo)[id] {
color: red;
}
:global(.foo)h1 {
color: red;
}
</style>
<div>
<h1 class="bar" id="baz">hello world</h1>
</div>
Loading…
Cancel
Save