Skip to content

Commit 5675067

Browse files
fix: allow combinator at start of nested CSS selector (#13440)
* fix: allow combinator at start of nested CSS selector Solved by moving the combinator positioning validation into the analysis phase Fixes #13433 * highlight the combinator, not the start of the combinator * fix --------- Co-authored-by: Rich Harris <[email protected]>
1 parent d77905d commit 5675067

File tree

9 files changed

+65
-8
lines changed

9 files changed

+65
-8
lines changed

.changeset/young-ducks-punch.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: allow combinator at start of nested CSS selector

packages/svelte/src/compiler/phases/1-parse/read/style.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -351,11 +351,7 @@ function read_selector(parser, inside_pseudo_class = false) {
351351
const combinator = read_combinator(parser);
352352

353353
if (combinator) {
354-
if (relative_selector.selectors.length === 0) {
355-
if (!inside_pseudo_class) {
356-
e.css_selector_invalid(start);
357-
}
358-
} else {
354+
if (relative_selector.selectors.length > 0) {
359355
relative_selector.end = index;
360356
children.push(relative_selector);
361357
}

packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,17 @@ const css_visitors = {
129129
}
130130
},
131131
RelativeSelector(node, context) {
132+
const parent = /** @type {Css.ComplexSelector} */ (context.path.at(-1));
133+
134+
if (
135+
node.combinator != null &&
136+
!context.state.rule?.metadata.parent_rule &&
137+
parent.children[0] === node &&
138+
context.path.at(-3)?.type !== 'PseudoClassSelector'
139+
) {
140+
e.css_selector_invalid(node.combinator);
141+
}
142+
132143
node.metadata.is_global = node.selectors.length >= 1 && is_global(node);
133144

134145
if (node.selectors.length === 1) {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
warnings: [
5+
{
6+
code: 'css_unused_selector',
7+
end: {
8+
character: 109,
9+
column: 11,
10+
line: 11
11+
},
12+
message: 'Unused CSS selector "~ .unused"',
13+
start: {
14+
character: 100,
15+
column: 2,
16+
line: 11
17+
}
18+
}
19+
]
20+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
.foo.svelte-xyz {
3+
> .bar:where(.svelte-xyz) {
4+
color: red;
5+
}
6+
7+
/* (unused) ~ .unused {
8+
color: red;
9+
}*/
10+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<div class="foo">
2+
<div class="bar"></div>
3+
</div>
4+
5+
<style>
6+
.foo {
7+
> .bar {
8+
color: red;
9+
}
10+
11+
~ .unused {
12+
color: red;
13+
}
14+
}
15+
</style>

packages/svelte/tests/validator/samples/css-invalid-combinator-selector-1/errors.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
},
99
"end": {
1010
"line": 10,
11-
"column": 1
11+
"column": 2
1212
}
1313
}
1414
]

packages/svelte/tests/validator/samples/css-invalid-combinator-selector-2/errors.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
},
99
"end": {
1010
"line": 8,
11-
"column": 1
11+
"column": 2
1212
}
1313
}
1414
]

packages/svelte/tests/validator/samples/css-invalid-combinator-selector-3/errors.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
},
99
"end": {
1010
"line": 5,
11-
"column": 2
11+
"column": 3
1212
}
1313
}
1414
]

0 commit comments

Comments
 (0)