@@ -10,6 +10,7 @@ import { get_attribute_chunks, is_text_attribute } from '../../../utils/ast.js';
1010 * stylesheet: Compiler.Css.StyleSheet;
1111 * element: Compiler.AST.RegularElement | Compiler.AST.SvelteElement;
1212 * from_render_tag: boolean;
13+ * in_root_selector: boolean;
1314 * }} State
1415 */
1516/** @typedef {NODE_PROBABLY_EXISTS | NODE_DEFINITELY_EXISTS } NodeExistsValue */
@@ -61,9 +62,17 @@ export function prune(stylesheet, element) {
6162 const parent = get_element_parent ( element ) ;
6263 if ( ! parent ) return ;
6364
64- walk ( stylesheet , { stylesheet, element : parent , from_render_tag : true } , visitors ) ;
65+ walk (
66+ stylesheet ,
67+ { stylesheet, element : parent , from_render_tag : true , in_root_selector : false } ,
68+ visitors
69+ ) ;
6570 } else {
66- walk ( stylesheet , { stylesheet, element, from_render_tag : false } , visitors ) ;
71+ walk (
72+ stylesheet ,
73+ { stylesheet, element, from_render_tag : false , in_root_selector : false } ,
74+ visitors
75+ ) ;
6776 }
6877}
6978
@@ -79,6 +88,16 @@ const visitors = {
7988 ComplexSelector ( node , context ) {
8089 const selectors = get_relative_selectors ( node ) ;
8190 const inner = selectors [ selectors . length - 1 ] ;
91+ /** @type {State } */
92+ const state = {
93+ ...context . state ,
94+ in_root_selector : [
95+ node ,
96+ ...get_parent_rules ( node . metadata . rule ) . flatMap ( ( r ) => r . prelude . children )
97+ ] . some ( ( c ) =>
98+ c . children [ 0 ] . selectors . some ( ( s ) => s . type === 'PseudoClassSelector' && s . name === 'root' )
99+ )
100+ } ;
82101
83102 if ( context . state . from_render_tag ) {
84103 // We're searching for a match that crosses a render tag boundary. That means we have to both traverse up
@@ -98,7 +117,7 @@ const visitors = {
98117 selectors_to_check ,
99118 /** @type {Compiler.Css.Rule } */ ( node . metadata . rule ) ,
100119 element ,
101- context . state
120+ state
102121 )
103122 ) {
104123 mark ( inner , element ) ;
@@ -114,7 +133,7 @@ const visitors = {
114133 selectors ,
115134 /** @type {Compiler.Css.Rule } */ ( node . metadata . rule ) ,
116135 context . state . element ,
117- context . state
136+ state
118137 )
119138 ) {
120139 mark ( inner , context . state . element ) ;
@@ -127,6 +146,21 @@ const visitors = {
127146 }
128147} ;
129148
149+ /**
150+ * @param {Compiler.Css.Rule | null } rule
151+ */
152+ function get_parent_rules ( rule ) {
153+ const parents = [ ] ;
154+
155+ let parent = rule ?. metadata . parent_rule ;
156+ while ( parent ) {
157+ parents . push ( parent ) ;
158+ parent = parent . metadata . parent_rule ;
159+ }
160+
161+ return parents ;
162+ }
163+
130164/**
131165 * Retrieves the relative selectors (minus the trailing globals) from a complex selector.
132166 * Also searches them for any existing `&` selectors and adds one if none are found.
@@ -198,15 +232,13 @@ function truncate(node) {
198232
199233 return node . children . slice ( 0 , i + 1 ) . map ( ( child ) => {
200234 // In case of `:root.y:has(...)`, `y` is unscoped, but everything in `:has(...)` should be scoped (if not global).
201- // To properly accomplish that, we gotta filter out all selector types except `:has` and `:root `.
235+ // To properly accomplish that, we gotta filter out all selector types `:has`.
202236 const root = child . selectors . find ( ( s ) => s . type === 'PseudoClassSelector' && s . name === 'root' ) ;
203237 if ( ! root || child . metadata . is_global_like ) return child ;
204238
205239 return {
206240 ...child ,
207- selectors : child . selectors . filter (
208- ( s ) => s . type === 'PseudoClassSelector' && ( s . name === 'has' || s . name === 'root' )
209- )
241+ selectors : child . selectors . filter ( ( s ) => s . type === 'PseudoClassSelector' && s . name === 'has' )
210242 } ;
211243 } ) ;
212244}
@@ -428,11 +460,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
428460 let sibling_elements ; // do them lazy because it's rarely used and expensive to calculate
429461
430462 // If this is a :has on a :root, we gotta include the element itself, too, because everything's a descendant of :root
431- const is_root_selector = other_selectors . some (
432- ( s ) => s . type === 'PseudoClassSelector' && s . name === 'root'
433- ) ;
434-
435- if ( is_root_selector ) {
463+ if ( state . in_root_selector ) {
436464 child_elements . push ( element ) ;
437465 descendant_elements . push ( element ) ;
438466 }
@@ -482,7 +510,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
482510
483511 const descendants =
484512 left_most_combinator . name === '+' || left_most_combinator . name === '~'
485- ? ( sibling_elements ??= get_following_sibling_elements ( element , is_root_selector ) )
513+ ? ( sibling_elements ??= get_following_sibling_elements ( element , state . in_root_selector ) )
486514 : left_most_combinator . name === '>'
487515 ? child_elements
488516 : descendant_elements ;
@@ -529,9 +557,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
529557
530558 switch ( selector . type ) {
531559 case 'PseudoClassSelector' : {
532- if ( name === 'host' ) return false ;
533-
534- if ( name === 'root' ) break ;
560+ if ( name === 'host' || name === 'root' ) return false ;
535561
536562 if (
537563 name === 'global' &&
0 commit comments