@@ -9,6 +9,7 @@ import { get_attribute_chunks, is_text_attribute } from '../../../utils/ast.js';
9
9
* @typedef {{
10
10
* stylesheet: Compiler.Css.StyleSheet;
11
11
* element: Compiler.AST.RegularElement | Compiler.AST.SvelteElement;
12
+ * from_render_tag: boolean;
12
13
* }} State
13
14
*/
14
15
/** @typedef {NODE_PROBABLY_EXISTS | NODE_DEFINITELY_EXISTS } NodeExistsValue */
@@ -53,10 +54,17 @@ const nesting_selector = {
53
54
/**
54
55
*
55
56
* @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
57
58
*/
58
59
export 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
+ }
60
68
}
61
69
62
70
/** @type {Visitors<Compiler.Css.Node, State> } */
@@ -101,7 +109,37 @@ const visitors = {
101
109
}
102
110
}
103
111
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 (
105
143
apply_selector (
106
144
selectors ,
107
145
/** @type {Compiler.Css.Rule } */ ( node . metadata . rule ) ,
@@ -144,17 +182,9 @@ function truncate(node) {
144
182
* @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement } element
145
183
* @param {Compiler.Css.StyleSheet } stylesheet
146
184
* @param {boolean } check_has Whether or not to check the `:has(...)` selectors
147
- * @param {boolean } [contains_render_tag]
148
185
* @returns {boolean }
149
186
*/
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 ) {
158
188
const parent_selectors = relative_selectors . slice ( ) ;
159
189
const relative_selector = parent_selectors . pop ( ) ;
160
190
@@ -169,19 +199,7 @@ function apply_selector(
169
199
) ;
170
200
171
201
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 ;
185
203
}
186
204
187
205
if ( relative_selector . combinator ) {
@@ -196,10 +214,6 @@ function apply_selector(
196
214
) ;
197
215
}
198
216
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
-
203
217
// if this is the left-most non-global selector, mark it — we want
204
218
// `x y z {...}` to become `x.blah y z.blah {...}`
205
219
const parent = parent_selectors [ parent_selectors . length - 1 ] ;
@@ -705,7 +719,7 @@ function unquote(str) {
705
719
}
706
720
707
721
/**
708
- * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement } node
722
+ * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.RenderTag } node
709
723
* @returns {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | null }
710
724
*/
711
725
function get_element_parent ( node ) {
0 commit comments