From a6e416da8c8cd5c262b24c25fd66995fa97252b9 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 21 Oct 2024 18:41:07 +0200 Subject: [PATCH] fix: mark `:has` selectors with multiple preceding selectors as used (#13750) Fixes #13717 There are two parts to this: 1. the parent selectors weren't passed along for the check inside `:has`, which in case of a leading combinator would mean it would always count as unused 2. In case if a selector like `x > y:has(z)`, the prior logic would correctly determine that for element `z` there's a match for the `:has` selector, by first checking its contents and then walking up the tree. But after it did that, it would try to walk up the tree once more, which is a) wasteful b) buggy because the tree walking mechanism would no longer be adjusted for the `:has` special case, resulting in false negatives. To fix that, the `:has` will return a new value from the function, signaling that it already fully checked the upper selectors, and so the function calling it will skip doing that. --- .changeset/five-zoos-brush.md | 5 +++++ .../src/compiler/phases/2-analyze/css/css-prune.js | 14 +++++++++++--- packages/svelte/tests/css/samples/has/expected.css | 3 +++ packages/svelte/tests/css/samples/has/input.svelte | 3 +++ 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 .changeset/five-zoos-brush.md diff --git a/.changeset/five-zoos-brush.md b/.changeset/five-zoos-brush.md new file mode 100644 index 000000000000..ff28e81ae2c8 --- /dev/null +++ b/.changeset/five-zoos-brush.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: mark `:has` selectors with multiple preceding selectors as used 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 64cbf28891c6..f65d1c281dce 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 @@ -202,12 +202,17 @@ function apply_selector(relative_selectors, rule, element, stylesheet, check_has const possible_match = relative_selector_might_apply_to_node( relative_selector, + parent_selectors, rule, element, stylesheet, check_has ); + if (possible_match === 'definite_match') { + return true; + } + if (!possible_match) { return false; } @@ -388,14 +393,16 @@ const regex_backslash_and_following_character = /\\(.)/g; * Ensure that `element` satisfies each simple selector in `relative_selector` * * @param {Compiler.Css.RelativeSelector} relative_selector + * @param {Compiler.Css.RelativeSelector[]} parent_selectors * @param {Compiler.Css.Rule} rule * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement} element * @param {Compiler.Css.StyleSheet} stylesheet * @param {boolean} check_has Whether or not to check the `:has(...)` selectors - * @returns {boolean} + * @returns {boolean | 'definite_match'} */ function relative_selector_might_apply_to_node( relative_selector, + parent_selectors, rule, element, stylesheet, @@ -446,7 +453,7 @@ function relative_selector_might_apply_to_node( apply_combinator( left_most_combinator, selectors[0] ?? [], - [relative_selector], + [...parent_selectors, relative_selector], rule, element, stylesheet, @@ -478,7 +485,8 @@ function relative_selector_might_apply_to_node( } } - return true; + // We return this to signal the parent "don't bother checking the rest of the selectors, I already did that" + return 'definite_match'; } for (const selector of other_selectors) { diff --git a/packages/svelte/tests/css/samples/has/expected.css b/packages/svelte/tests/css/samples/has/expected.css index a543916631fd..8b01b2a74957 100644 --- a/packages/svelte/tests/css/samples/has/expected.css +++ b/packages/svelte/tests/css/samples/has/expected.css @@ -85,3 +85,6 @@ /* (unused) x:has(> z) { color: red; }*/ + x.svelte-xyz > y:where(.svelte-xyz):has(z:where(.svelte-xyz)) { + color: green; + } diff --git a/packages/svelte/tests/css/samples/has/input.svelte b/packages/svelte/tests/css/samples/has/input.svelte index 0a9644d2ae16..2cbd06b95a08 100644 --- a/packages/svelte/tests/css/samples/has/input.svelte +++ b/packages/svelte/tests/css/samples/has/input.svelte @@ -91,4 +91,7 @@ x:has(> z) { color: red; } + x > y:has(z) { + color: green; + }