fix: correctly look for sibling elements inside blocks and components (#15106)

Fixes #15027 and fixes #14995

When the target element is inside a block or component, get_following_sibling_elements was looking only for sibling elements after the block/component. This PR fixes it by starting the search from the begging of the block/component and skipping nodes that go before the target element.
pull/15139/head
7nik 8 months ago committed by GitHub
parent 5e9b29c351
commit 9410ad0318
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: correctly look for sibling elements inside blocks and components when pruning CSS

@ -638,19 +638,30 @@ function get_following_sibling_elements(element, include_self) {
/** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */ /** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */
const siblings = []; const siblings = [];
// ...then walk them, starting from the node after the one // ...then walk them, starting from the node containing the element in question
// containing the element in question // skipping nodes that appears before the element
const seen = new Set(); const seen = new Set();
let skip = true;
/** @param {Compiler.AST.SvelteNode} node */ /** @param {Compiler.AST.SvelteNode} node */
function get_siblings(node) { function get_siblings(node) {
walk(node, null, { walk(node, null, {
RegularElement(node) { RegularElement(node) {
siblings.push(node); if (node === element) {
skip = false;
if (include_self) siblings.push(node);
} else if (!skip) {
siblings.push(node);
}
}, },
SvelteElement(node) { SvelteElement(node) {
siblings.push(node); if (node === element) {
skip = false;
if (include_self) siblings.push(node);
} else if (!skip) {
siblings.push(node);
}
}, },
RenderTag(node) { RenderTag(node) {
for (const snippet of node.metadata.snippets) { for (const snippet of node.metadata.snippets) {
@ -663,14 +674,10 @@ function get_following_sibling_elements(element, include_self) {
}); });
} }
for (const node of nodes.slice(nodes.indexOf(start) + 1)) { for (const node of nodes.slice(nodes.indexOf(start))) {
get_siblings(node); get_siblings(node);
} }
if (include_self) {
siblings.push(element);
}
return siblings; return siblings;
} }

@ -6,182 +6,196 @@ export default test({
code: 'css_unused_selector', code: 'css_unused_selector',
message: 'Unused CSS selector ".unused:has(y)"', message: 'Unused CSS selector ".unused:has(y)"',
start: { start: {
line: 31, line: 33,
column: 1, column: 1,
character: 308 character: 330
}, },
end: { end: {
line: 31, line: 33,
column: 15, column: 15,
character: 322 character: 344
} }
}, },
{ {
code: 'css_unused_selector', code: 'css_unused_selector',
message: 'Unused CSS selector ".unused:has(:global(y))"', message: 'Unused CSS selector ".unused:has(:global(y))"',
start: { start: {
line: 34, line: 36,
column: 1, column: 1,
character: 343 character: 365
}, },
end: { end: {
line: 34, line: 36,
column: 24, column: 24,
character: 366 character: 388
} }
}, },
{ {
code: 'css_unused_selector', code: 'css_unused_selector',
message: 'Unused CSS selector "x:has(.unused)"', message: 'Unused CSS selector "x:has(.unused)"',
start: { start: {
line: 37, line: 39,
column: 1, column: 1,
character: 387 character: 409
}, },
end: { end: {
line: 37, line: 39,
column: 15, column: 15,
character: 401 character: 423
} }
}, },
{ {
code: 'css_unused_selector', code: 'css_unused_selector',
message: 'Unused CSS selector ":global(.foo):has(.unused)"', message: 'Unused CSS selector ":global(.foo):has(.unused)"',
start: { start: {
line: 40, line: 42,
column: 1, column: 1,
character: 422 character: 444
}, },
end: { end: {
line: 40, line: 42,
column: 27, column: 27,
character: 448 character: 470
} }
}, },
{ {
code: 'css_unused_selector', code: 'css_unused_selector',
message: 'Unused CSS selector "x:has(y):has(.unused)"', message: 'Unused CSS selector "x:has(y):has(.unused)"',
start: { start: {
line: 50, line: 52,
column: 1, column: 1,
character: 556 character: 578
}, },
end: { end: {
line: 50, line: 52,
column: 22, column: 22,
character: 577 character: 599
} }
}, },
{ {
code: 'css_unused_selector', code: 'css_unused_selector',
message: 'Unused CSS selector ".unused"', message: 'Unused CSS selector ".unused"',
start: { start: {
line: 69, line: 71,
column: 2, column: 2,
character: 782 character: 804
}, },
end: { end: {
line: 69, line: 71,
column: 9, column: 9,
character: 789 character: 811
} }
}, },
{ {
code: 'css_unused_selector', code: 'css_unused_selector',
message: 'Unused CSS selector ".unused x:has(y)"', message: 'Unused CSS selector ".unused x:has(y)"',
start: { start: {
line: 85, line: 87,
column: 1, column: 1,
character: 936 character: 958
}, },
end: { end: {
line: 85, line: 87,
column: 17, column: 17,
character: 952 character: 974
} }
}, },
{ {
code: 'css_unused_selector', code: 'css_unused_selector',
message: 'Unused CSS selector ".unused:has(.unused)"', message: 'Unused CSS selector ".unused:has(.unused)"',
start: { start: {
line: 88, line: 90,
column: 1, column: 1,
character: 973 character: 995
}, },
end: { end: {
line: 88, line: 90,
column: 21, column: 21,
character: 993 character: 1015
} }
}, },
{ {
code: 'css_unused_selector', code: 'css_unused_selector',
message: 'Unused CSS selector "x:has(> z)"', message: 'Unused CSS selector "x:has(> z)"',
start: { start: {
line: 98, line: 100,
column: 1, column: 1,
character: 1093 character: 1115
}, },
end: { end: {
line: 98, line: 100,
column: 11, column: 11,
character: 1103 character: 1125
} }
}, },
{ {
code: 'css_unused_selector', code: 'css_unused_selector',
message: 'Unused CSS selector "x:has(> d)"', message: 'Unused CSS selector "x:has(> d)"',
start: { start: {
line: 101, line: 103,
column: 1, column: 1,
character: 1124 character: 1146
}, },
end: { end: {
line: 101, line: 103,
column: 11, column: 11,
character: 1134 character: 1156
} }
}, },
{ {
code: 'css_unused_selector', code: 'css_unused_selector',
message: 'Unused CSS selector "x:has(~ y)"', message: 'Unused CSS selector "x:has(~ y)"',
start: { start: {
line: 121, line: 123,
column: 1, column: 1,
character: 1326 character: 1348
}, },
end: { end: {
line: 121, line: 123,
column: 11, column: 11,
character: 1336 character: 1358
}
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector "f:has(~ d)"',
start: {
line: 133,
column: 1,
character: 1446
},
end: {
line: 133,
column: 11,
character: 1456
} }
}, },
{ {
code: 'css_unused_selector', code: 'css_unused_selector',
message: 'Unused CSS selector ":has(.unused)"', message: 'Unused CSS selector ":has(.unused)"',
start: { start: {
line: 129, line: 141,
column: 2, column: 2,
character: 1409 character: 1529
}, },
end: { end: {
line: 129, line: 141,
column: 15, column: 15,
character: 1422 character: 1542
} }
}, },
{ {
code: 'css_unused_selector', code: 'css_unused_selector',
message: 'Unused CSS selector "&:has(.unused)"', message: 'Unused CSS selector "&:has(.unused)"',
start: { start: {
line: 135, line: 147,
column: 2, column: 2,
character: 1480 character: 1600
}, },
end: { end: {
line: 135, line: 147,
column: 16, column: 16,
character: 1494 character: 1614
} }
} }
] ]

@ -112,6 +112,16 @@
color: red; color: red;
}*/ }*/
d.svelte-xyz:has(+ e:where(.svelte-xyz)) {
color: green;
}
d.svelte-xyz:has(~ f:where(.svelte-xyz)) {
color: green;
}
/* (unused) f:has(~ d) {
color: red;
}*/
.foo { .foo {
.svelte-xyz:has(x:where(.svelte-xyz)) { .svelte-xyz:has(x:where(.svelte-xyz)) {
color: green; color: green;

@ -3,6 +3,8 @@
<z></z> <z></z>
{#if foo} {#if foo}
<d></d> <d></d>
<e></e>
<f></f>
{/if} {/if}
</y> </y>
</x> </x>
@ -122,6 +124,16 @@
color: red; color: red;
} }
d:has(+ e) {
color: green;
}
d:has(~ f) {
color: green;
}
f:has(~ d) {
color: red;
}
:global(.foo) { :global(.foo) {
:has(x) { :has(x) {
color: green; color: green;

Loading…
Cancel
Save