@@ -9,6 +9,7 @@ import { get_attribute_chunks, is_text_attribute } from '../../../utils/ast.js';
99 * @typedef {{
1010 * stylesheet: Compiler.Css.StyleSheet;
1111 * element: Compiler.AST.RegularElement | Compiler.AST.SvelteElement;
12+ * from_render_tag: boolean;
1213 * }} State
1314 */
1415/** @typedef {NODE_PROBABLY_EXISTS | NODE_DEFINITELY_EXISTS } NodeExistsValue */
@@ -53,10 +54,17 @@ const nesting_selector = {
5354/**
5455 *
5556 * @param {Compiler.Css.StyleSheet } stylesheet
56- * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement } element
57+ * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.RenderTag } element
5758 */
5859export function prune ( stylesheet , element ) {
59- walk ( stylesheet , { stylesheet, element } , visitors ) ;
60+ if ( element . type === 'RenderTag' ) {
61+ const parent = get_element_parent ( element ) ;
62+ if ( ! parent ) return ;
63+
64+ walk ( stylesheet , { stylesheet, element : parent , from_render_tag : true } , visitors ) ;
65+ } else {
66+ walk ( stylesheet , { stylesheet, element, from_render_tag : false } , visitors ) ;
67+ }
6068}
6169
6270/** @type {Visitors<Compiler.Css.Node, State> } */
@@ -101,7 +109,37 @@ const visitors = {
101109 }
102110 }
103111
104- if (
112+ if ( context . state . from_render_tag ) {
113+ // We're searching for a match that crosses a render tag boundary. That means we have to both traverse up
114+ // the element tree (to see if we find an entry point) but also remove selectors from the end (assuming
115+ // they are part of the render tag we don't see). We do all possible combinations of both until we find a match.
116+ /** @type {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | null } */
117+ let element = context . state . element ;
118+
119+ while ( element ) {
120+ const selectors_to_check = selectors . slice ( ) ;
121+
122+ while ( selectors_to_check . length > 0 ) {
123+ selectors_to_check . pop ( ) ;
124+
125+ if (
126+ apply_selector (
127+ selectors_to_check ,
128+ /** @type {Compiler.Css.Rule } */ ( node . metadata . rule ) ,
129+ element ,
130+ context . state . stylesheet ,
131+ true
132+ )
133+ ) {
134+ mark ( inner , element ) ;
135+ node . metadata . used = true ;
136+ return ;
137+ }
138+ }
139+
140+ element = get_element_parent ( element ) ;
141+ }
142+ } else if (
105143 apply_selector (
106144 selectors ,
107145 /** @type {Compiler.Css.Rule } */ ( node . metadata . rule ) ,
@@ -144,17 +182,9 @@ function truncate(node) {
144182 * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement } element
145183 * @param {Compiler.Css.StyleSheet } stylesheet
146184 * @param {boolean } check_has Whether or not to check the `:has(...)` selectors
147- * @param {boolean } [contains_render_tag]
148185 * @returns {boolean }
149186 */
150- function apply_selector (
151- relative_selectors ,
152- rule ,
153- element ,
154- stylesheet ,
155- check_has ,
156- contains_render_tag
157- ) {
187+ function apply_selector ( relative_selectors , rule , element , stylesheet , check_has ) {
158188 const parent_selectors = relative_selectors . slice ( ) ;
159189 const relative_selector = parent_selectors . pop ( ) ;
160190
@@ -169,19 +199,7 @@ function apply_selector(
169199 ) ;
170200
171201 if ( ! possible_match ) {
172- contains_render_tag ??= element . fragment . nodes . some ( ( node ) => node . type === 'RenderTag' ) ;
173- if ( contains_render_tag ) {
174- // If the element contains a render tag then we assume the selector might match something inside the rendered snippet
175- // and traverse the blocks upwards to see if the present blocks match our node further upwards.
176- // (We could do more static analysis and check the render tag reference to see if this snippet block continues
177- // with elements that actually match the selector, but that would be a lot of work for little gain)
178- const possible = apply_selector ( parent_selectors , rule , element , stylesheet , true ) ;
179- if ( possible ) return true ; // e.g `div span` matched for element `<div>{@render tag()}</div>`
180- // Continue checking if a parent element might match the selector.
181- // Example: Selector is `p span`, which matches `<p><strong>{@render tag()}</strong></p>` and we're currently at `strong`
182- } else {
183- return false ;
184- }
202+ return false ;
185203 }
186204
187205 if ( relative_selector . combinator ) {
@@ -196,10 +214,6 @@ function apply_selector(
196214 ) ;
197215 }
198216
199- // We got to the end of this under the assumption higher up might start matching,
200- // but turns out it didn't - therefore the selector doesn't apply after all.
201- if ( contains_render_tag ) return false ;
202-
203217 // if this is the left-most non-global selector, mark it — we want
204218 // `x y z {...}` to become `x.blah y z.blah {...}`
205219 const parent = parent_selectors [ parent_selectors . length - 1 ] ;
@@ -705,7 +719,7 @@ function unquote(str) {
705719}
706720
707721/**
708- * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement } node
722+ * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.RenderTag } node
709723 * @returns {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | null }
710724 */
711725function get_element_parent ( node ) {
0 commit comments