From e035e2838faf26da476e73831fb0e555464bf1d1 Mon Sep 17 00:00:00 2001 From: 7nik Date: Mon, 31 Mar 2025 22:37:27 +0300 Subject: [PATCH] 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 --- .changeset/nervous-humans-flash.md | 5 +++ .../phases/2-analyze/css/css-prune.js | 26 +++++++++---- .../_config.js | 24 +++--------- .../expected.css | 4 +- .../input.svelte | 4 +- .../Child.svelte | 5 +++ .../siblings-combinator-component/_config.js | 20 ++++++++++ .../expected.css | 8 ++++ .../input.svelte | 37 +++++++++++++++++++ .../siblings-combinator-slot/_config.js | 10 +---- .../siblings-combinator-slot/expected.css | 2 +- .../siblings-combinator-slot/input.svelte | 2 +- 12 files changed, 108 insertions(+), 39 deletions(-) create mode 100644 .changeset/nervous-humans-flash.md create mode 100644 packages/svelte/tests/css/samples/siblings-combinator-component/Child.svelte create mode 100644 packages/svelte/tests/css/samples/siblings-combinator-component/_config.js create mode 100644 packages/svelte/tests/css/samples/siblings-combinator-component/expected.css create mode 100644 packages/svelte/tests/css/samples/siblings-combinator-component/input.svelte diff --git a/.changeset/nervous-humans-flash.md b/.changeset/nervous-humans-flash.md new file mode 100644 index 0000000000..753bb91e3f --- /dev/null +++ b/.changeset/nervous-humans-flash.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: better consider component and its snippets during css pruning diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index 070ec7cd34..0646c6341a 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -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()}

foo

` 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} seen - * @returns {Map} + * @returns {Map} */ function get_possible_element_siblings(node, direction, adjacent_only, seen = new Set()) { - /** @type {Map} */ + /** @type {Map} */ 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} 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} NodeMap */ diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/_config.js b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/_config.js index 97e470d1c3..7644865495 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/_config.js +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/_config.js @@ -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 } } ] }); diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/expected.css b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/expected.css index 67a19d10c9..53fca3ae9e 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/expected.css +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/expected.css @@ -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; }*/ diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte index 2e2846fa87..52264d3a5a 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte @@ -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; }
diff --git a/packages/svelte/tests/css/samples/siblings-combinator-component/Child.svelte b/packages/svelte/tests/css/samples/siblings-combinator-component/Child.svelte new file mode 100644 index 0000000000..1df9f35e50 --- /dev/null +++ b/packages/svelte/tests/css/samples/siblings-combinator-component/Child.svelte @@ -0,0 +1,5 @@ + + +{@render foo()} diff --git a/packages/svelte/tests/css/samples/siblings-combinator-component/_config.js b/packages/svelte/tests/css/samples/siblings-combinator-component/_config.js new file mode 100644 index 0000000000..837fa20ae1 --- /dev/null +++ b/packages/svelte/tests/css/samples/siblings-combinator-component/_config.js @@ -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 + } + } + ] +}); diff --git a/packages/svelte/tests/css/samples/siblings-combinator-component/expected.css b/packages/svelte/tests/css/samples/siblings-combinator-component/expected.css new file mode 100644 index 0000000000..d2657ccd21 --- /dev/null +++ b/packages/svelte/tests/css/samples/siblings-combinator-component/expected.css @@ -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; }*/ diff --git a/packages/svelte/tests/css/samples/siblings-combinator-component/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-component/input.svelte new file mode 100644 index 0000000000..8d80acffb3 --- /dev/null +++ b/packages/svelte/tests/css/samples/siblings-combinator-component/input.svelte @@ -0,0 +1,37 @@ + + +
+ + + + {#snippet foo()} + + {/snippet} + + + + + + + + {#snippet foo()} + + + + {/snippet} + + +
+ + diff --git a/packages/svelte/tests/css/samples/siblings-combinator-slot/_config.js b/packages/svelte/tests/css/samples/siblings-combinator-slot/_config.js index 2786baeff8..8a8f561d01 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-slot/_config.js +++ b/packages/svelte/tests/css/samples/siblings-combinator-slot/_config.js @@ -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 } } ] }); diff --git a/packages/svelte/tests/css/samples/siblings-combinator-slot/expected.css b/packages/svelte/tests/css/samples/siblings-combinator-slot/expected.css index 643f6cf13f..85cbb77e65 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-slot/expected.css +++ b/packages/svelte/tests/css/samples/siblings-combinator-slot/expected.css @@ -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; }*/ diff --git a/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte index 1b543f97b7..57e1df1507 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte +++ b/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte @@ -5,10 +5,10 @@