@@ -127,8 +127,7 @@ const visitors = {
127127 selectors_to_check ,
128128 /** @type {Compiler.Css.Rule } */ ( node . metadata . rule ) ,
129129 element ,
130- context . state . stylesheet ,
131- true
130+ context . state . stylesheet
132131 )
133132 ) {
134133 mark ( inner , element ) ;
@@ -144,8 +143,7 @@ const visitors = {
144143 selectors ,
145144 /** @type {Compiler.Css.Rule } */ ( node . metadata . rule ) ,
146145 context . state . element ,
147- context . state . stylesheet ,
148- true
146+ context . state . stylesheet
149147 )
150148 ) {
151149 mark ( inner , context . state . element ) ;
@@ -191,28 +189,21 @@ function truncate(node) {
191189 * @param {Compiler.Css.Rule } rule
192190 * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement } element
193191 * @param {Compiler.Css.StyleSheet } stylesheet
194- * @param {boolean } check_has Whether or not to check the `:has(...)` selectors
195192 * @returns {boolean }
196193 */
197- function apply_selector ( relative_selectors , rule , element , stylesheet , check_has ) {
194+ function apply_selector ( relative_selectors , rule , element , stylesheet ) {
198195 const parent_selectors = relative_selectors . slice ( ) ;
199196 const relative_selector = parent_selectors . pop ( ) ;
200197
201198 if ( ! relative_selector ) return false ;
202199
203200 const possible_match = relative_selector_might_apply_to_node (
204201 relative_selector ,
205- parent_selectors ,
206202 rule ,
207203 element ,
208- stylesheet ,
209- check_has
204+ stylesheet
210205 ) ;
211206
212- if ( possible_match === 'definite_match' ) {
213- return true ;
214- }
215-
216207 if ( ! possible_match ) {
217208 return false ;
218209 }
@@ -224,8 +215,7 @@ function apply_selector(relative_selectors, rule, element, stylesheet, check_has
224215 parent_selectors ,
225216 rule ,
226217 element ,
227- stylesheet ,
228- check_has
218+ stylesheet
229219 ) ;
230220 }
231221
@@ -246,7 +236,6 @@ function apply_selector(relative_selectors, rule, element, stylesheet, check_has
246236 * @param {Compiler.Css.Rule } rule
247237 * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement } element
248238 * @param {Compiler.Css.StyleSheet } stylesheet
249- * @param {boolean } check_has Whether or not to check the `:has(...)` selectors
250239 * @returns {boolean }
251240 */
252241function apply_combinator (
@@ -255,8 +244,7 @@ function apply_combinator(
255244 parent_selectors ,
256245 rule ,
257246 element ,
258- stylesheet ,
259- check_has
247+ stylesheet
260248) {
261249 const name = combinator . name ;
262250
@@ -281,7 +269,7 @@ function apply_combinator(
281269 }
282270
283271 if ( parent . type === 'RegularElement' || parent . type === 'SvelteElement' ) {
284- if ( apply_selector ( parent_selectors , rule , parent , stylesheet , check_has ) ) {
272+ if ( apply_selector ( parent_selectors , rule , parent , stylesheet ) ) {
285273 // TODO the `name === ' '` causes false positives, but removing it causes false negatives...
286274 if ( name === ' ' || crossed_component_boundary ) {
287275 mark ( parent_selectors [ parent_selectors . length - 1 ] , parent ) ;
@@ -312,9 +300,7 @@ function apply_combinator(
312300 mark ( relative_selector , element ) ;
313301 sibling_matched = true ;
314302 }
315- } else if (
316- apply_selector ( parent_selectors , rule , possible_sibling , stylesheet , check_has )
317- ) {
303+ } else if ( apply_selector ( parent_selectors , rule , possible_sibling , stylesheet ) ) {
318304 mark ( relative_selector , element ) ;
319305 sibling_matched = true ;
320306 }
@@ -393,21 +379,12 @@ const regex_backslash_and_following_character = /\\(.)/g;
393379 * Ensure that `element` satisfies each simple selector in `relative_selector`
394380 *
395381 * @param {Compiler.Css.RelativeSelector } relative_selector
396- * @param {Compiler.Css.RelativeSelector[] } parent_selectors
397382 * @param {Compiler.Css.Rule } rule
398383 * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement } element
399384 * @param {Compiler.Css.StyleSheet } stylesheet
400- * @param {boolean } check_has Whether or not to check the `:has(...)` selectors
401- * @returns {boolean | 'definite_match' }
385+ * @returns {boolean }
402386 */
403- function relative_selector_might_apply_to_node (
404- relative_selector ,
405- parent_selectors ,
406- rule ,
407- element ,
408- stylesheet ,
409- check_has
410- ) {
387+ function relative_selector_might_apply_to_node ( relative_selector , rule , element , stylesheet ) {
411388 // Sort :has(...) selectors in one bucket and everything else into another
412389 const has_selectors = [ ] ;
413390 const other_selectors = [ ] ;
@@ -422,7 +399,26 @@ function relative_selector_might_apply_to_node(
422399
423400 // If we're called recursively from a :has(...) selector, we're on the way of checking if the other selectors match.
424401 // In that case ignore this check (because we just came from this) to avoid an infinite loop.
425- if ( check_has && has_selectors . length > 0 ) {
402+ if ( has_selectors . length > 0 ) {
403+ /** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement> } */
404+ const child_elements = [ ] ;
405+ /** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement> } */
406+ const descendant_elements = [ ] ;
407+
408+ /**
409+ * @param {Compiler.SvelteNode } node
410+ * @param {boolean } is_recursing
411+ */
412+ function collect_child_elements ( node , is_recursing ) {
413+ if ( node . type === 'RegularElement' || node . type === 'SvelteElement' ) {
414+ descendant_elements . push ( node ) ;
415+ if ( ! is_recursing ) child_elements . push ( node ) ;
416+ node . fragment . nodes . forEach ( ( node ) => collect_child_elements ( node , true ) ) ;
417+ }
418+ }
419+
420+ element . fragment . nodes . forEach ( ( node ) => collect_child_elements ( node , false ) ) ;
421+
426422 // :has(...) is special in that it means "look downwards in the CSS tree". Since our matching algorithm goes
427423 // upwards and back-to-front, we need to first check the selectors inside :has(...), then check the rest of the
428424 // selector in a way that is similar to ancestor matching. In a sense, we're treating `.x:has(.y)` as `.x .y`.
@@ -443,25 +439,20 @@ function relative_selector_might_apply_to_node(
443439 } ;
444440 }
445441
446- if (
447- selectors . length === 0 /* is :global(...) */ ||
448- apply_selector ( selectors , rule , element , stylesheet , check_has )
449- ) {
450- // Treat e.g. `.x:has(.y)` as `.x .y` with the .y part already being matched,
451- // and now looking upwards for the .x part.
442+ const descendants =
443+ left_most_combinator . name === '>' ? child_elements : descendant_elements ;
444+
445+ let selector_matched = false ;
446+
447+ // Iterate over all descendant elements and check if the selector inside :has matches
448+ for ( const element of descendants ) {
452449 if (
453- apply_combinator (
454- left_most_combinator ,
455- selectors [ 0 ] ?? [ ] ,
456- [ ...parent_selectors , relative_selector ] ,
457- rule ,
458- element ,
459- stylesheet ,
460- false
461- )
450+ selectors . length === 0 /* is :global(...) */ ||
451+ ( element . metadata . scoped && selector_matched ) ||
452+ apply_selector ( selectors , rule , element , stylesheet )
462453 ) {
463454 complex_selector . metadata . used = true ;
464- matched = true ;
455+ selector_matched = matched = true ;
465456 }
466457 }
467458 }
@@ -484,9 +475,6 @@ function relative_selector_might_apply_to_node(
484475 return false ;
485476 }
486477 }
487-
488- // We return this to signal the parent "don't bother checking the rest of the selectors, I already did that"
489- return 'definite_match' ;
490478 }
491479
492480 for ( const selector of other_selectors ) {
@@ -507,7 +495,7 @@ function relative_selector_might_apply_to_node(
507495 ) {
508496 const args = selector . args ;
509497 const complex_selector = args . children [ 0 ] ;
510- return apply_selector ( complex_selector . children , rule , element , stylesheet , check_has ) ;
498+ return apply_selector ( complex_selector . children , rule , element , stylesheet ) ;
511499 }
512500
513501 // We came across a :global, everything beyond it is global and therefore a potential match
@@ -520,7 +508,7 @@ function relative_selector_might_apply_to_node(
520508 const relative = truncate ( complex_selector ) ;
521509 if (
522510 relative . length === 0 /* is :global(...) */ ||
523- apply_selector ( relative , rule , element , stylesheet , check_has )
511+ apply_selector ( relative , rule , element , stylesheet )
524512 ) {
525513 complex_selector . metadata . used = true ;
526514 matched = true ;
@@ -621,7 +609,7 @@ function relative_selector_might_apply_to_node(
621609 const parent = /** @type {Compiler.Css.Rule } */ ( rule . metadata . parent_rule ) ;
622610
623611 for ( const complex_selector of parent . prelude . children ) {
624- if ( apply_selector ( truncate ( complex_selector ) , parent , element , stylesheet , check_has ) ) {
612+ if ( apply_selector ( truncate ( complex_selector ) , parent , element , stylesheet ) ) {
625613 complex_selector . metadata . used = true ;
626614 matched = true ;
627615 }
0 commit comments