@ -127,8 +127,7 @@ const visitors = {
selectors _to _check ,
/** @type {Compiler.Css.Rule} */ ( node . metadata . rule ) ,
element ,
context . state . stylesheet ,
true
context . state . stylesheet
)
) {
mark ( inner , element ) ;
@ -144,8 +143,7 @@ const visitors = {
selectors ,
/** @type {Compiler.Css.Rule} */ ( node . metadata . rule ) ,
context . state . element ,
context . state . stylesheet ,
true
context . state . stylesheet
)
) {
mark ( inner , context . state . element ) ;
@ -191,10 +189,9 @@ function truncate(node) {
* @ 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 }
* /
function apply _selector ( relative _selectors , rule , element , stylesheet , check _has ) {
function apply _selector ( relative _selectors , rule , element , stylesheet ) {
const parent _selectors = relative _selectors . slice ( ) ;
const relative _selector = parent _selectors . pop ( ) ;
@ -202,17 +199,11 @@ 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
stylesheet
) ;
if ( possible _match === 'definite_match' ) {
return true ;
}
if ( ! possible _match ) {
return false ;
}
@ -224,8 +215,7 @@ function apply_selector(relative_selectors, rule, element, stylesheet, check_has
parent _selectors ,
rule ,
element ,
stylesheet ,
check _has
stylesheet
) ;
}
@ -246,7 +236,6 @@ function apply_selector(relative_selectors, rule, element, stylesheet, check_has
* @ 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 }
* /
function apply _combinator (
@ -255,8 +244,7 @@ function apply_combinator(
parent _selectors ,
rule ,
element ,
stylesheet ,
check _has
stylesheet
) {
const name = combinator . name ;
@ -281,7 +269,7 @@ function apply_combinator(
}
if ( parent . type === 'RegularElement' || parent . type === 'SvelteElement' ) {
if ( apply _selector ( parent _selectors , rule , parent , stylesheet , check _has )) {
if ( apply _selector ( parent _selectors , rule , parent , stylesheet )) {
// TODO the `name === ' '` causes false positives, but removing it causes false negatives...
if ( name === ' ' || crossed _component _boundary ) {
mark ( parent _selectors [ parent _selectors . length - 1 ] , parent ) ;
@ -312,9 +300,7 @@ function apply_combinator(
mark ( relative _selector , element ) ;
sibling _matched = true ;
}
} else if (
apply _selector ( parent _selectors , rule , possible _sibling , stylesheet , check _has )
) {
} else if ( apply _selector ( parent _selectors , rule , possible _sibling , stylesheet ) ) {
mark ( relative _selector , element ) ;
sibling _matched = true ;
}
@ -393,21 +379,12 @@ 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 | 'definite_match' }
* @ returns { boolean }
* /
function relative _selector _might _apply _to _node (
relative _selector ,
parent _selectors ,
rule ,
element ,
stylesheet ,
check _has
) {
function relative _selector _might _apply _to _node ( relative _selector , rule , element , stylesheet ) {
// Sort :has(...) selectors in one bucket and everything else into another
const has _selectors = [ ] ;
const other _selectors = [ ] ;
@ -422,7 +399,26 @@ function relative_selector_might_apply_to_node(
// If we're called recursively from a :has(...) selector, we're on the way of checking if the other selectors match.
// In that case ignore this check (because we just came from this) to avoid an infinite loop.
if ( check _has && has _selectors . length > 0 ) {
if ( has _selectors . length > 0 ) {
/** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */
const child _elements = [ ] ;
/** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */
const descendant _elements = [ ] ;
/ * *
* @ param { Compiler . SvelteNode } node
* @ param { boolean } is _recursing
* /
function collect _child _elements ( node , is _recursing ) {
if ( node . type === 'RegularElement' || node . type === 'SvelteElement' ) {
descendant _elements . push ( node ) ;
if ( ! is _recursing ) child _elements . push ( node ) ;
node . fragment . nodes . forEach ( ( node ) => collect _child _elements ( node , true ) ) ;
}
}
element . fragment . nodes . forEach ( ( node ) => collect _child _elements ( node , false ) ) ;
// :has(...) is special in that it means "look downwards in the CSS tree". Since our matching algorithm goes
// upwards and back-to-front, we need to first check the selectors inside :has(...), then check the rest of the
// 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(
} ;
}
const descendants =
left _most _combinator . name === '>' ? child _elements : descendant _elements ;
let selector _matched = false ;
// Iterate over all descendant elements and check if the selector inside :has matches
for ( const element of descendants ) {
if (
selectors . length === 0 /* is :global(...) */ ||
apply _selector ( selectors , rule , element , stylesheet , check _has )
) {
// Treat e.g. `.x:has(.y)` as `.x .y` with the .y part already being matched,
// and now looking upwards for the .x part.
if (
apply _combinator (
left _most _combinator ,
selectors [ 0 ] ? ? [ ] ,
[ ... parent _selectors , relative _selector ] ,
rule ,
element ,
stylesheet ,
false
)
( element . metadata . scoped && selector _matched ) ||
apply _selector ( selectors , rule , element , stylesheet )
) {
complex _selector . metadata . used = true ;
matched = true ;
selector _matched = matched = true ;
}
}
}
@ -484,9 +475,6 @@ function relative_selector_might_apply_to_node(
return false ;
}
}
// 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 ) {
@ -507,7 +495,7 @@ function relative_selector_might_apply_to_node(
) {
const args = selector . args ;
const complex _selector = args . children [ 0 ] ;
return apply _selector ( complex _selector . children , rule , element , stylesheet , check _has );
return apply _selector ( complex _selector . children , rule , element , stylesheet );
}
// 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(
const relative = truncate ( complex _selector ) ;
if (
relative . length === 0 /* is :global(...) */ ||
apply _selector ( relative , rule , element , stylesheet , check _has )
apply _selector ( relative , rule , element , stylesheet )
) {
complex _selector . metadata . used = true ;
matched = true ;
@ -621,7 +609,7 @@ function relative_selector_might_apply_to_node(
const parent = /** @type {Compiler.Css.Rule} */ ( rule . metadata . parent _rule ) ;
for ( const complex _selector of parent . prelude . children ) {
if ( apply _selector ( truncate ( complex _selector ) , parent , element , stylesheet , check _has )) {
if ( apply _selector ( truncate ( complex _selector ) , parent , element , stylesheet )) {
complex _selector . metadata . used = true ;
matched = true ;
}