@@ -127,8 +127,7 @@ const visitors = {
127
127
selectors_to_check ,
128
128
/** @type {Compiler.Css.Rule } */ ( node . metadata . rule ) ,
129
129
element ,
130
- context . state . stylesheet ,
131
- true
130
+ context . state . stylesheet
132
131
)
133
132
) {
134
133
mark ( inner , element ) ;
@@ -144,8 +143,7 @@ const visitors = {
144
143
selectors ,
145
144
/** @type {Compiler.Css.Rule } */ ( node . metadata . rule ) ,
146
145
context . state . element ,
147
- context . state . stylesheet ,
148
- true
146
+ context . state . stylesheet
149
147
)
150
148
) {
151
149
mark ( inner , context . state . element ) ;
@@ -191,28 +189,21 @@ function truncate(node) {
191
189
* @param {Compiler.Css.Rule } rule
192
190
* @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement } element
193
191
* @param {Compiler.Css.StyleSheet } stylesheet
194
- * @param {boolean } check_has Whether or not to check the `:has(...)` selectors
195
192
* @returns {boolean }
196
193
*/
197
- function apply_selector ( relative_selectors , rule , element , stylesheet , check_has ) {
194
+ function apply_selector ( relative_selectors , rule , element , stylesheet ) {
198
195
const parent_selectors = relative_selectors . slice ( ) ;
199
196
const relative_selector = parent_selectors . pop ( ) ;
200
197
201
198
if ( ! relative_selector ) return false ;
202
199
203
200
const possible_match = relative_selector_might_apply_to_node (
204
201
relative_selector ,
205
- parent_selectors ,
206
202
rule ,
207
203
element ,
208
- stylesheet ,
209
- check_has
204
+ stylesheet
210
205
) ;
211
206
212
- if ( possible_match === 'definite_match' ) {
213
- return true ;
214
- }
215
-
216
207
if ( ! possible_match ) {
217
208
return false ;
218
209
}
@@ -224,8 +215,7 @@ function apply_selector(relative_selectors, rule, element, stylesheet, check_has
224
215
parent_selectors ,
225
216
rule ,
226
217
element ,
227
- stylesheet ,
228
- check_has
218
+ stylesheet
229
219
) ;
230
220
}
231
221
@@ -246,7 +236,6 @@ function apply_selector(relative_selectors, rule, element, stylesheet, check_has
246
236
* @param {Compiler.Css.Rule } rule
247
237
* @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement } element
248
238
* @param {Compiler.Css.StyleSheet } stylesheet
249
- * @param {boolean } check_has Whether or not to check the `:has(...)` selectors
250
239
* @returns {boolean }
251
240
*/
252
241
function apply_combinator (
@@ -255,8 +244,7 @@ function apply_combinator(
255
244
parent_selectors ,
256
245
rule ,
257
246
element ,
258
- stylesheet ,
259
- check_has
247
+ stylesheet
260
248
) {
261
249
const name = combinator . name ;
262
250
@@ -281,7 +269,7 @@ function apply_combinator(
281
269
}
282
270
283
271
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 ) ) {
285
273
// TODO the `name === ' '` causes false positives, but removing it causes false negatives...
286
274
if ( name === ' ' || crossed_component_boundary ) {
287
275
mark ( parent_selectors [ parent_selectors . length - 1 ] , parent ) ;
@@ -312,9 +300,7 @@ function apply_combinator(
312
300
mark ( relative_selector , element ) ;
313
301
sibling_matched = true ;
314
302
}
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 ) ) {
318
304
mark ( relative_selector , element ) ;
319
305
sibling_matched = true ;
320
306
}
@@ -393,21 +379,12 @@ const regex_backslash_and_following_character = /\\(.)/g;
393
379
* Ensure that `element` satisfies each simple selector in `relative_selector`
394
380
*
395
381
* @param {Compiler.Css.RelativeSelector } relative_selector
396
- * @param {Compiler.Css.RelativeSelector[] } parent_selectors
397
382
* @param {Compiler.Css.Rule } rule
398
383
* @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement } element
399
384
* @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 }
402
386
*/
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 ) {
411
388
// Sort :has(...) selectors in one bucket and everything else into another
412
389
const has_selectors = [ ] ;
413
390
const other_selectors = [ ] ;
@@ -422,7 +399,26 @@ function relative_selector_might_apply_to_node(
422
399
423
400
// If we're called recursively from a :has(...) selector, we're on the way of checking if the other selectors match.
424
401
// 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
+
426
422
// :has(...) is special in that it means "look downwards in the CSS tree". Since our matching algorithm goes
427
423
// upwards and back-to-front, we need to first check the selectors inside :has(...), then check the rest of the
428
424
// 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(
443
439
} ;
444
440
}
445
441
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 ) {
452
449
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 )
462
453
) {
463
454
complex_selector . metadata . used = true ;
464
- matched = true ;
455
+ selector_matched = matched = true ;
465
456
}
466
457
}
467
458
}
@@ -484,9 +475,6 @@ function relative_selector_might_apply_to_node(
484
475
return false ;
485
476
}
486
477
}
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' ;
490
478
}
491
479
492
480
for ( const selector of other_selectors ) {
@@ -507,7 +495,7 @@ function relative_selector_might_apply_to_node(
507
495
) {
508
496
const args = selector . args ;
509
497
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 ) ;
511
499
}
512
500
513
501
// 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(
520
508
const relative = truncate ( complex_selector ) ;
521
509
if (
522
510
relative . length === 0 /* is :global(...) */ ||
523
- apply_selector ( relative , rule , element , stylesheet , check_has )
511
+ apply_selector ( relative , rule , element , stylesheet )
524
512
) {
525
513
complex_selector . metadata . used = true ;
526
514
matched = true ;
@@ -621,7 +609,7 @@ function relative_selector_might_apply_to_node(
621
609
const parent = /** @type {Compiler.Css.Rule } */ ( rule . metadata . parent_rule ) ;
622
610
623
611
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 ) ) {
625
613
complex_selector . metadata . used = true ;
626
614
matched = true ;
627
615
}
0 commit comments