@@ -10,6 +10,7 @@ import { get_attribute_chunks, is_text_attribute } from '../../../utils/ast.js';
10
10
* stylesheet: Compiler.Css.StyleSheet;
11
11
* element: Compiler.AST.RegularElement | Compiler.AST.SvelteElement;
12
12
* from_render_tag: boolean;
13
+ * inside_not: boolean;
13
14
* }} State
14
15
*/
15
16
/** @typedef {NODE_PROBABLY_EXISTS | NODE_DEFINITELY_EXISTS } NodeExistsValue */
@@ -61,9 +62,13 @@ export function prune(stylesheet, element) {
61
62
const parent = get_element_parent ( element ) ;
62
63
if ( ! parent ) return ;
63
64
64
- walk ( stylesheet , { stylesheet, element : parent , from_render_tag : true } , visitors ) ;
65
+ walk (
66
+ stylesheet ,
67
+ { stylesheet, element : parent , from_render_tag : true , inside_not : false } ,
68
+ visitors
69
+ ) ;
65
70
} else {
66
- walk ( stylesheet , { stylesheet, element, from_render_tag : false } , visitors ) ;
71
+ walk ( stylesheet , { stylesheet, element, from_render_tag : false , inside_not : false } , visitors ) ;
67
72
}
68
73
}
69
74
@@ -127,7 +132,7 @@ const visitors = {
127
132
selectors_to_check ,
128
133
/** @type {Compiler.Css.Rule } */ ( node . metadata . rule ) ,
129
134
element ,
130
- context . state . stylesheet
135
+ context . state
131
136
)
132
137
) {
133
138
mark ( inner , element ) ;
@@ -143,7 +148,7 @@ const visitors = {
143
148
selectors ,
144
149
/** @type {Compiler.Css.Rule } */ ( node . metadata . rule ) ,
145
150
context . state . element ,
146
- context . state . stylesheet
151
+ context . state
147
152
)
148
153
) {
149
154
mark ( inner , context . state . element ) ;
@@ -188,10 +193,10 @@ function truncate(node) {
188
193
* @param {Compiler.Css.RelativeSelector[] } relative_selectors
189
194
* @param {Compiler.Css.Rule } rule
190
195
* @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement } element
191
- * @param {Compiler.Css.StyleSheet } stylesheet
196
+ * @param {State } state
192
197
* @returns {boolean }
193
198
*/
194
- function apply_selector ( relative_selectors , rule , element , stylesheet ) {
199
+ function apply_selector ( relative_selectors , rule , element , state ) {
195
200
const parent_selectors = relative_selectors . slice ( ) ;
196
201
const relative_selector = parent_selectors . pop ( ) ;
197
202
@@ -201,7 +206,7 @@ function apply_selector(relative_selectors, rule, element, stylesheet) {
201
206
relative_selector ,
202
207
rule ,
203
208
element ,
204
- stylesheet
209
+ state
205
210
) ;
206
211
207
212
if ( ! possible_match ) {
@@ -215,14 +220,14 @@ function apply_selector(relative_selectors, rule, element, stylesheet) {
215
220
parent_selectors ,
216
221
rule ,
217
222
element ,
218
- stylesheet
223
+ state
219
224
) ;
220
225
}
221
226
222
227
// if this is the left-most non-global selector, mark it — we want
223
228
// `x y z {...}` to become `x.blah y z.blah {...}`
224
229
const parent = parent_selectors [ parent_selectors . length - 1 ] ;
225
- if ( ! parent || is_global ( parent , rule ) ) {
230
+ if ( ! state . inside_not && ( ! parent || is_global ( parent , rule ) ) ) {
226
231
mark ( relative_selector , element ) ;
227
232
}
228
233
@@ -235,17 +240,10 @@ function apply_selector(relative_selectors, rule, element, stylesheet) {
235
240
* @param {Compiler.Css.RelativeSelector[] } parent_selectors
236
241
* @param {Compiler.Css.Rule } rule
237
242
* @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement } element
238
- * @param {Compiler.Css.StyleSheet } stylesheet
243
+ * @param {State } state
239
244
* @returns {boolean }
240
245
*/
241
- function apply_combinator (
242
- combinator ,
243
- relative_selector ,
244
- parent_selectors ,
245
- rule ,
246
- element ,
247
- stylesheet
248
- ) {
246
+ function apply_combinator ( combinator , relative_selector , parent_selectors , rule , element , state ) {
249
247
const name = combinator . name ;
250
248
251
249
switch ( name ) {
@@ -269,9 +267,9 @@ function apply_combinator(
269
267
}
270
268
271
269
if ( parent . type === 'RegularElement' || parent . type === 'SvelteElement' ) {
272
- if ( apply_selector ( parent_selectors , rule , parent , stylesheet ) ) {
270
+ if ( apply_selector ( parent_selectors , rule , parent , state ) ) {
273
271
// TODO the `name === ' '` causes false positives, but removing it causes false negatives...
274
- if ( name === ' ' || crossed_component_boundary ) {
272
+ if ( ! state . inside_not && ( name === ' ' || crossed_component_boundary ) ) {
275
273
mark ( parent_selectors [ parent_selectors . length - 1 ] , parent ) ;
276
274
}
277
275
@@ -297,11 +295,15 @@ function apply_combinator(
297
295
if ( possible_sibling . type === 'RenderTag' || possible_sibling . type === 'SlotElement' ) {
298
296
// `{@render foo()}<p>foo</p>` with `:global(.x) + p` is a match
299
297
if ( parent_selectors . length === 1 && parent_selectors [ 0 ] . metadata . is_global ) {
300
- mark ( relative_selector , element ) ;
298
+ if ( ! state . inside_not ) {
299
+ mark ( relative_selector , element ) ;
300
+ }
301
301
sibling_matched = true ;
302
302
}
303
- } else if ( apply_selector ( parent_selectors , rule , possible_sibling , stylesheet ) ) {
304
- mark ( relative_selector , element ) ;
303
+ } else if ( apply_selector ( parent_selectors , rule , possible_sibling , state ) ) {
304
+ if ( ! state . inside_not ) {
305
+ mark ( relative_selector , element ) ;
306
+ }
305
307
sibling_matched = true ;
306
308
}
307
309
}
@@ -381,10 +383,10 @@ const regex_backslash_and_following_character = /\\(.)/g;
381
383
* @param {Compiler.Css.RelativeSelector } relative_selector
382
384
* @param {Compiler.Css.Rule } rule
383
385
* @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement } element
384
- * @param {Compiler.Css.StyleSheet } stylesheet
386
+ * @param {State } state
385
387
* @returns {boolean }
386
388
*/
387
- function relative_selector_might_apply_to_node ( relative_selector , rule , element , stylesheet ) {
389
+ function relative_selector_might_apply_to_node ( relative_selector , rule , element , state ) {
388
390
// Sort :has(...) selectors in one bucket and everything else into another
389
391
const has_selectors = [ ] ;
390
392
const other_selectors = [ ] ;
@@ -458,7 +460,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
458
460
if (
459
461
selectors . length === 0 /* is :global(...) */ ||
460
462
( element . metadata . scoped && selector_matched ) ||
461
- apply_selector ( selectors , rule , element , stylesheet )
463
+ apply_selector ( selectors , rule , element , state )
462
464
) {
463
465
complex_selector . metadata . used = true ;
464
466
selector_matched = matched = true ;
@@ -504,7 +506,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
504
506
) {
505
507
const args = selector . args ;
506
508
const complex_selector = args . children [ 0 ] ;
507
- return apply_selector ( complex_selector . children , rule , element , stylesheet ) ;
509
+ return apply_selector ( complex_selector . children , rule , element , state ) ;
508
510
}
509
511
510
512
// We came across a :global, everything beyond it is global and therefore a potential match
@@ -517,15 +519,25 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
517
519
const relative = truncate ( complex_selector ) ;
518
520
const is_global = relative . length === 0 ;
519
521
520
- if ( is_global || apply_selector ( relative , rule , element , stylesheet ) ) {
522
+ if ( is_global ) {
523
+ complex_selector . metadata . used = true ;
524
+ matched = true ;
525
+ } else if ( name !== 'not' && apply_selector ( relative , rule , element , state ) ) {
521
526
complex_selector . metadata . used = true ;
522
527
matched = true ;
523
- } else if ( name === 'not' ) {
528
+ } else if (
529
+ name === 'not' &&
530
+ ! apply_selector ( relative , rule , element , { ...state , inside_not : true } )
531
+ ) {
524
532
// For `:not(...)` we gotta do the inverse: If it did not match, mark the element and possibly
525
533
// everything above (if the selector is written is a such) as scoped (because they matched by not matching).
526
- // The above if condition will ensure the selector itself will be marked as used if it doesn't match at least once,
527
- // and therefore having actual usefulness in the CSS output.
528
534
element . metadata . scoped = true ;
535
+ complex_selector . metadata . used = true ;
536
+ matched = true ;
537
+
538
+ for ( const r of relative ) {
539
+ r . metadata . scoped = true ;
540
+ }
529
541
530
542
// bar:not(foo bar) means that foo is an ancestor of bar
531
543
if ( complex_selector . children . length > 1 ) {
@@ -633,7 +645,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
633
645
const parent = /** @type {Compiler.Css.Rule } */ ( rule . metadata . parent_rule ) ;
634
646
635
647
for ( const complex_selector of parent . prelude . children ) {
636
- if ( apply_selector ( truncate ( complex_selector ) , parent , element , stylesheet ) ) {
648
+ if ( apply_selector ( truncate ( complex_selector ) , parent , element , state ) ) {
637
649
complex_selector . metadata . used = true ;
638
650
matched = true ;
639
651
}
0 commit comments