1
1
import { get_possible_values } from './gather_possible_values.js' ;
2
2
import { regex_starts_with_whitespace , regex_ends_with_whitespace } from '../../patterns.js' ;
3
3
import { error } from '../../../errors.js' ;
4
+ import { Stylesheet } from './Stylesheet.js' ;
4
5
5
6
const NO_MATCH = 'NO_MATCH' ;
6
7
const POSSIBLE_MATCH = 'POSSIBLE_MATCH' ;
@@ -22,7 +23,7 @@ export default class Selector {
22
23
/** @type {import('#compiler').Css.Selector } */
23
24
node ;
24
25
25
- /** @type {import('./Stylesheet.js').default } */
26
+ /** @type {import('./Stylesheet.js').Stylesheet } */
26
27
stylesheet ;
27
28
28
29
/** @type {Block[] } */
@@ -31,39 +32,28 @@ export default class Selector {
31
32
/** @type {Block[] } */
32
33
local_blocks ;
33
34
34
- /** @type {boolean } */
35
- used ;
35
+ used = false ;
36
36
37
37
/**
38
38
* @param {import('#compiler').Css.Selector } node
39
- * @param {import('./Stylesheet.js').default } stylesheet
39
+ * @param {import('./Stylesheet.js').Stylesheet } stylesheet
40
40
*/
41
41
constructor ( node , stylesheet ) {
42
42
this . node = node ;
43
43
this . stylesheet = stylesheet ;
44
44
this . blocks = group_selectors ( node ) ;
45
45
// take trailing :global(...) selectors out of consideration
46
- let i = this . blocks . length ;
47
- while ( i > 0 ) {
48
- if ( ! this . blocks [ i - 1 ] . global ) break ;
49
- i -= 1 ;
50
- }
51
- this . local_blocks = this . blocks . slice ( 0 , i ) ;
52
- const host_only = this . blocks . length === 1 && this . blocks [ 0 ] . host ;
53
- const root_only = this . blocks . length === 1 && this . blocks [ 0 ] . root ;
54
- this . used = this . local_blocks . length === 0 || host_only || root_only ;
46
+ const i = this . blocks . findLastIndex ( ( block ) => ! block . can_ignore ( ) ) ;
47
+ this . local_blocks = this . blocks . slice ( 0 , i + 1 ) ;
48
+
49
+ // if we have a `:root {...}` or `:global(...) {...}` selector, we need to mark
50
+ // this selector as `used` even if the component doesn't contain any nodes
51
+ this . used = this . local_blocks . length === 0 ;
55
52
}
56
53
57
54
/** @param {import('#compiler').RegularElement | import('#compiler').SvelteElement } node */
58
55
apply ( node ) {
59
- /** @type {Array<{ node: import('#compiler').RegularElement | import('#compiler').SvelteElement; block: Block }> } */
60
- const to_encapsulate = [ ] ;
61
- apply_selector ( this . local_blocks . slice ( ) , node , to_encapsulate ) ;
62
- if ( to_encapsulate . length > 0 ) {
63
- to_encapsulate . forEach ( ( { node, block } ) => {
64
- this . stylesheet . nodes_with_css_class . add ( node ) ;
65
- block . should_encapsulate = true ;
66
- } ) ;
56
+ if ( apply_selector ( this . local_blocks . slice ( ) , node , this . stylesheet ) ) {
67
57
this . used = true ;
68
58
}
69
59
}
@@ -130,6 +120,13 @@ export default class Selector {
130
120
131
121
/** @param {import('../../types.js').ComponentAnalysis } analysis */
132
122
validate ( analysis ) {
123
+ this . validate_global_placement ( ) ;
124
+ this . validate_global_with_multiple_selectors ( ) ;
125
+ this . validate_global_compound_selector ( ) ;
126
+ this . validate_invalid_combinator_without_selector ( analysis ) ;
127
+ }
128
+
129
+ validate_global_placement ( ) {
133
130
let start = 0 ;
134
131
let end = this . blocks . length ;
135
132
for ( ; start < end ; start += 1 ) {
@@ -143,9 +140,6 @@ export default class Selector {
143
140
error ( this . blocks [ i ] . selectors [ 0 ] , 'invalid-css-global-placement' ) ;
144
141
}
145
142
}
146
- this . validate_global_with_multiple_selectors ( ) ;
147
- this . validate_global_compound_selector ( ) ;
148
- this . validate_invalid_combinator_without_selector ( analysis ) ;
149
143
}
150
144
151
145
validate_global_with_multiple_selectors ( ) {
@@ -207,10 +201,10 @@ export default class Selector {
207
201
/**
208
202
* @param {Block[] } blocks
209
203
* @param {import('#compiler').RegularElement | import('#compiler').SvelteElement | null } node
210
- * @param {Array<{ node: import('#compiler').RegularElement | import('#compiler').SvelteElement; block: Block }> } to_encapsulate
204
+ * @param {Stylesheet } stylesheet
211
205
* @returns {boolean }
212
206
*/
213
- function apply_selector ( blocks , node , to_encapsulate ) {
207
+ function apply_selector ( blocks , node , stylesheet ) {
214
208
const block = blocks . pop ( ) ;
215
209
if ( ! block ) return false ;
216
210
if ( ! node ) {
@@ -224,43 +218,52 @@ function apply_selector(blocks, node, to_encapsulate) {
224
218
return false ;
225
219
}
226
220
227
- if ( applies === UNKNOWN_SELECTOR ) {
228
- to_encapsulate . push ( { node, block } ) ;
221
+ /**
222
+ * Mark both the compound selector and the node it selects as encapsulated,
223
+ * for transformation in a later step
224
+ * @param {Block } block
225
+ * @param {import('#compiler').RegularElement | import('#compiler').SvelteElement } node
226
+ */
227
+ function mark ( block , node ) {
228
+ block . should_encapsulate = true ;
229
+ stylesheet . nodes_with_css_class . add ( node ) ;
229
230
return true ;
230
231
}
231
232
233
+ if ( applies === UNKNOWN_SELECTOR ) {
234
+ return mark ( block , node ) ;
235
+ }
236
+
232
237
if ( block . combinator ) {
233
238
if ( block . combinator . type === 'Combinator' && block . combinator . name === ' ' ) {
234
239
for ( const ancestor_block of blocks ) {
235
240
if ( ancestor_block . global ) {
236
241
continue ;
237
242
}
238
243
if ( ancestor_block . host ) {
239
- to_encapsulate . push ( { node, block } ) ;
240
- return true ;
244
+ return mark ( block , node ) ;
241
245
}
242
246
/** @type {import('#compiler').RegularElement | import('#compiler').SvelteElement | null } */
243
247
let parent = node ;
248
+ let matched = false ;
244
249
while ( ( parent = get_element_parent ( parent ) ) ) {
245
250
if ( block_might_apply_to_node ( ancestor_block , parent ) !== NO_MATCH ) {
246
- to_encapsulate . push ( { node : parent , block : ancestor_block } ) ;
251
+ mark ( ancestor_block , parent ) ;
252
+ matched = true ;
247
253
}
248
254
}
249
- if ( to_encapsulate . length ) {
250
- to_encapsulate . push ( { node, block } ) ;
251
- return true ;
255
+ if ( matched ) {
256
+ return mark ( block , node ) ;
252
257
}
253
258
}
254
259
if ( blocks . every ( ( block ) => block . global ) ) {
255
- to_encapsulate . push ( { node, block } ) ;
256
- return true ;
260
+ return mark ( block , node ) ;
257
261
}
258
262
return false ;
259
263
} else if ( block . combinator . name === '>' ) {
260
264
const has_global_parent = blocks . every ( ( block ) => block . global ) ;
261
- if ( has_global_parent || apply_selector ( blocks , get_element_parent ( node ) , to_encapsulate ) ) {
262
- to_encapsulate . push ( { node, block } ) ;
263
- return true ;
265
+ if ( has_global_parent || apply_selector ( blocks , get_element_parent ( node ) , stylesheet ) ) {
266
+ return mark ( block , node ) ;
264
267
}
265
268
return false ;
266
269
} else if ( block . combinator . name === '+' || block . combinator . name === '~' ) {
@@ -274,23 +277,22 @@ function apply_selector(blocks, node, to_encapsulate) {
274
277
if ( siblings . size === 0 && get_element_parent ( node ) !== null ) {
275
278
return false ;
276
279
}
277
- to_encapsulate . push ( { node, block } ) ;
278
- return true ;
280
+ return mark ( block , node ) ;
279
281
}
280
282
for ( const possible_sibling of siblings . keys ( ) ) {
281
- if ( apply_selector ( blocks . slice ( ) , possible_sibling , to_encapsulate ) ) {
282
- to_encapsulate . push ( { node , block } ) ;
283
+ if ( apply_selector ( blocks . slice ( ) , possible_sibling , stylesheet ) ) {
284
+ mark ( block , node ) ;
283
285
has_match = true ;
284
286
}
285
287
}
286
288
return has_match ;
287
289
}
290
+
288
291
// TODO other combinators
289
- to_encapsulate . push ( { node, block } ) ;
290
- return true ;
292
+ return mark ( block , node ) ;
291
293
}
292
- to_encapsulate . push ( { node , block } ) ;
293
- return true ;
294
+
295
+ return mark ( block , node ) ;
294
296
}
295
297
296
298
const regex_backslash_and_following_character = / \\ ( .) / g;
@@ -815,6 +817,11 @@ class Block {
815
817
this . selectors . push ( selector ) ;
816
818
this . end = selector . end ;
817
819
}
820
+
821
+ can_ignore ( ) {
822
+ return this . global || this . host || this . root ;
823
+ }
824
+
818
825
get global ( ) {
819
826
return (
820
827
this . selectors . length >= 1 &&
0 commit comments