Fix: consider component and its snippets during css pruning (#15630)

* fix: consider component and its snippets during css pruning

* add changeset

* fix: avoid iterator.map

---------

Co-authored-by: 7nik <kifiranet@gmail.com>
pull/15659/head
7nik 6 months ago committed by GitHub
parent c822f9b0bf
commit e035e2838f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: better consider component and its snippets during css pruning

@ -251,7 +251,11 @@ function apply_combinator(relative_selector, rest_selectors, rule, node, directi
let sibling_matched = false;
for (const possible_sibling of siblings.keys()) {
if (possible_sibling.type === 'RenderTag' || possible_sibling.type === 'SlotElement') {
if (
possible_sibling.type === 'RenderTag' ||
possible_sibling.type === 'SlotElement' ||
possible_sibling.type === 'Component'
) {
// `{@render foo()}<p>foo</p>` with `:global(.x) + p` is a match
if (rest_selectors.length === 1 && rest_selectors[0].metadata.is_global) {
sibling_matched = true;
@ -814,10 +818,10 @@ function get_element_parent(node) {
* @param {Direction} direction
* @param {boolean} adjacent_only
* @param {Set<Compiler.AST.SnippetBlock>} seen
* @returns {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.SlotElement | Compiler.AST.RenderTag, NodeExistsValue>}
* @returns {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.SlotElement | Compiler.AST.RenderTag | Compiler.AST.Component, NodeExistsValue>}
*/
function get_possible_element_siblings(node, direction, adjacent_only, seen = new Set()) {
/** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.SlotElement | Compiler.AST.RenderTag, NodeExistsValue>} */
/** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.SlotElement | Compiler.AST.RenderTag | Compiler.AST.Component, NodeExistsValue>} */
const result = new Map();
const path = node.metadata.path;
@ -847,14 +851,18 @@ function get_possible_element_siblings(node, direction, adjacent_only, seen = ne
}
// Special case: slots, render tags and svelte:element tags could resolve to no siblings,
// so we want to continue until we find a definite sibling even with the adjacent-only combinator
} else if (is_block(node)) {
if (node.type === 'SlotElement') {
} else if (is_block(node) || node.type === 'Component') {
if (node.type === 'SlotElement' || node.type === 'Component') {
result.set(node, NODE_PROBABLY_EXISTS);
}
const possible_last_child = get_possible_nested_siblings(node, direction, adjacent_only);
add_to_map(possible_last_child, result);
if (adjacent_only && has_definite_elements(possible_last_child)) {
if (
adjacent_only &&
node.type !== 'Component' &&
has_definite_elements(possible_last_child)
) {
return result;
}
} else if (node.type === 'SvelteElement') {
@ -907,7 +915,7 @@ function get_possible_element_siblings(node, direction, adjacent_only, seen = ne
}
/**
* @param {Compiler.AST.EachBlock | Compiler.AST.IfBlock | Compiler.AST.AwaitBlock | Compiler.AST.KeyBlock | Compiler.AST.SlotElement | Compiler.AST.SnippetBlock} node
* @param {Compiler.AST.EachBlock | Compiler.AST.IfBlock | Compiler.AST.AwaitBlock | Compiler.AST.KeyBlock | Compiler.AST.SlotElement | Compiler.AST.SnippetBlock | Compiler.AST.Component} node
* @param {Direction} direction
* @param {boolean} adjacent_only
* @param {Set<Compiler.AST.SnippetBlock>} seen
@ -942,6 +950,10 @@ function get_possible_nested_siblings(node, direction, adjacent_only, seen = new
seen.add(node);
fragments.push(node.body);
break;
case 'Component':
fragments.push(node.fragment, ...[...node.metadata.snippets].map((s) => s.body));
break;
}
/** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement, NodeExistsValue>} NodeMap */

@ -5,32 +5,20 @@ export default test({
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".b ~ .c"',
start: { character: 137, column: 1, line: 11 },
end: { character: 144, column: 8, line: 11 }
start: { character: 191, column: 1, line: 13 },
end: { character: 198, column: 8, line: 13 }
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".c ~ .f"',
start: { character: 162, column: 1, line: 12 },
end: { character: 169, column: 8, line: 12 }
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".f ~ .g"',
start: { character: 187, column: 1, line: 13 },
end: { character: 194, column: 8, line: 13 }
start: { character: 216, column: 1, line: 14 },
end: { character: 223, column: 8, line: 14 }
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".b ~ .f"',
start: { character: 212, column: 1, line: 14 },
end: { character: 219, column: 8, line: 14 }
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".b ~ .g"',
start: { character: 237, column: 1, line: 15 },
end: { character: 244, column: 8, line: 15 }
start: { character: 241, column: 1, line: 15 },
end: { character: 248, column: 8, line: 15 }
}
]
});

@ -2,10 +2,10 @@
.d.svelte-xyz ~ .e:where(.svelte-xyz) { color: green; }
.a.svelte-xyz ~ .g:where(.svelte-xyz) { color: green; }
.a.svelte-xyz ~ .b:where(.svelte-xyz) { color: green; }
.f.svelte-xyz ~ .g:where(.svelte-xyz) { color: green; }
.b.svelte-xyz ~ .g:where(.svelte-xyz) { color: green; }
/* no match */
/* (unused) .b ~ .c { color: red; }*/
/* (unused) .c ~ .f { color: red; }*/
/* (unused) .f ~ .g { color: red; }*/
/* (unused) .b ~ .f { color: red; }*/
/* (unused) .b ~ .g { color: red; }*/

@ -6,13 +6,13 @@
.d ~ .e { color: green; }
.a ~ .g { color: green; }
.a ~ .b { color: green; }
.f ~ .g { color: green; }
.b ~ .g { color: green; }
/* no match */
.b ~ .c { color: red; }
.c ~ .f { color: red; }
.f ~ .g { color: red; }
.b ~ .f { color: red; }
.b ~ .g { color: red; }
</style>
<div class="a"></div>

@ -0,0 +1,5 @@
<script>
let { foo } = $props();
</script>
{@render foo()}

@ -0,0 +1,20 @@
import { test } from '../../test';
export default test({
warnings: [
{
code: 'css_unused_selector',
message: 'Unused CSS selector "n + m"',
end: {
character: 468,
column: 6,
line: 36
},
start: {
character: 463,
column: 1,
line: 36
}
}
]
});

@ -0,0 +1,8 @@
x.svelte-xyz + y:where(.svelte-xyz) { color: green; }
x.svelte-xyz + v:where(.svelte-xyz) { color: green; }
x.svelte-xyz + z:where(.svelte-xyz) { color: green; }
y.svelte-xyz + z:where(.svelte-xyz) { color: green; }
v.svelte-xyz + z:where(.svelte-xyz) { color: green; }
.component + z.svelte-xyz { color: green; }
/* (unused) n + m { color: red; }*/

@ -0,0 +1,37 @@
<script>
import Child from './Child.svelte';
</script>
<div>
<x></x>
<Child>
<y></y>
{#snippet foo()}
<v></v>
{/snippet}
</Child>
<z></z>
<Child>
<span>
<n></n>
</span>
{#snippet foo()}
<span>
<n></n>
</span>
{/snippet}
</Child>
<m></m>
</div>
<style>
x + y { color: green; }
x + v { color: green; }
x + z { color: green; }
y + z { color: green; }
v + z { color: green; }
:global(.component) + z { color: green; }
n + m { color: red; }
</style>

@ -5,14 +5,8 @@ export default test({
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".b + .c"',
start: { character: 110, column: 1, line: 10 },
end: { character: 117, column: 8, line: 10 }
},
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".c + .f"',
start: { character: 135, column: 1, line: 11 },
end: { character: 142, column: 8, line: 11 }
start: { character: 137, column: 1, line: 11 },
end: { character: 144, column: 8, line: 11 }
}
]
});

@ -1,7 +1,7 @@
.d.svelte-xyz + .e:where(.svelte-xyz) { color: green; }
.a.svelte-xyz + .b:where(.svelte-xyz) { color: green; }
.c.svelte-xyz + .f:where(.svelte-xyz) { color: green; }
/* no match */
/* (unused) .b + .c { color: red; }*/
/* (unused) .c + .f { color: red; }*/

@ -5,10 +5,10 @@
<style>
.d + .e { color: green; }
.a + .b { color: green; }
.c + .f { color: green; }
/* no match */
.b + .c { color: red; }
.c + .f { color: red; }
</style>
<div class="a"></div>

Loading…
Cancel
Save