11import { get_possible_values } from './gather_possible_values.js' ;
22import { regex_starts_with_whitespace , regex_ends_with_whitespace } from '../../patterns.js' ;
33import { error } from '../../../errors.js' ;
4+ import { Stylesheet } from './Stylesheet.js' ;
45
56const NO_MATCH = 'NO_MATCH' ;
67const POSSIBLE_MATCH = 'POSSIBLE_MATCH' ;
@@ -22,7 +23,7 @@ export default class Selector {
2223 /** @type {import('#compiler').Css.Selector } */
2324 node ;
2425
25- /** @type {import('./Stylesheet.js').default } */
26+ /** @type {import('./Stylesheet.js').Stylesheet } */
2627 stylesheet ;
2728
2829 /** @type {Block[] } */
@@ -31,39 +32,28 @@ export default class Selector {
3132 /** @type {Block[] } */
3233 local_blocks ;
3334
34- /** @type {boolean } */
35- used ;
35+ used = false ;
3636
3737 /**
3838 * @param {import('#compiler').Css.Selector } node
39- * @param {import('./Stylesheet.js').default } stylesheet
39+ * @param {import('./Stylesheet.js').Stylesheet } stylesheet
4040 */
4141 constructor ( node , stylesheet ) {
4242 this . node = node ;
4343 this . stylesheet = stylesheet ;
4444 this . blocks = group_selectors ( node ) ;
4545 // 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 ;
5552 }
5653
5754 /** @param {import('#compiler').RegularElement | import('#compiler').SvelteElement } node */
5855 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 ) ) {
6757 this . used = true ;
6858 }
6959 }
@@ -130,6 +120,13 @@ export default class Selector {
130120
131121 /** @param {import('../../types.js').ComponentAnalysis } analysis */
132122 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 ( ) {
133130 let start = 0 ;
134131 let end = this . blocks . length ;
135132 for ( ; start < end ; start += 1 ) {
@@ -143,9 +140,6 @@ export default class Selector {
143140 error ( this . blocks [ i ] . selectors [ 0 ] , 'invalid-css-global-placement' ) ;
144141 }
145142 }
146- this . validate_global_with_multiple_selectors ( ) ;
147- this . validate_global_compound_selector ( ) ;
148- this . validate_invalid_combinator_without_selector ( analysis ) ;
149143 }
150144
151145 validate_global_with_multiple_selectors ( ) {
@@ -207,10 +201,10 @@ export default class Selector {
207201/**
208202 * @param {Block[] } blocks
209203 * @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
211205 * @returns {boolean }
212206 */
213- function apply_selector ( blocks , node , to_encapsulate ) {
207+ function apply_selector ( blocks , node , stylesheet ) {
214208 const block = blocks . pop ( ) ;
215209 if ( ! block ) return false ;
216210 if ( ! node ) {
@@ -224,43 +218,52 @@ function apply_selector(blocks, node, to_encapsulate) {
224218 return false ;
225219 }
226220
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 ) ;
229230 return true ;
230231 }
231232
233+ if ( applies === UNKNOWN_SELECTOR ) {
234+ return mark ( block , node ) ;
235+ }
236+
232237 if ( block . combinator ) {
233238 if ( block . combinator . type === 'Combinator' && block . combinator . name === ' ' ) {
234239 for ( const ancestor_block of blocks ) {
235240 if ( ancestor_block . global ) {
236241 continue ;
237242 }
238243 if ( ancestor_block . host ) {
239- to_encapsulate . push ( { node, block } ) ;
240- return true ;
244+ return mark ( block , node ) ;
241245 }
242246 /** @type {import('#compiler').RegularElement | import('#compiler').SvelteElement | null } */
243247 let parent = node ;
248+ let matched = false ;
244249 while ( ( parent = get_element_parent ( parent ) ) ) {
245250 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 ;
247253 }
248254 }
249- if ( to_encapsulate . length ) {
250- to_encapsulate . push ( { node, block } ) ;
251- return true ;
255+ if ( matched ) {
256+ return mark ( block , node ) ;
252257 }
253258 }
254259 if ( blocks . every ( ( block ) => block . global ) ) {
255- to_encapsulate . push ( { node, block } ) ;
256- return true ;
260+ return mark ( block , node ) ;
257261 }
258262 return false ;
259263 } else if ( block . combinator . name === '>' ) {
260264 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 ) ;
264267 }
265268 return false ;
266269 } else if ( block . combinator . name === '+' || block . combinator . name === '~' ) {
@@ -274,23 +277,22 @@ function apply_selector(blocks, node, to_encapsulate) {
274277 if ( siblings . size === 0 && get_element_parent ( node ) !== null ) {
275278 return false ;
276279 }
277- to_encapsulate . push ( { node, block } ) ;
278- return true ;
280+ return mark ( block , node ) ;
279281 }
280282 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 ) ;
283285 has_match = true ;
284286 }
285287 }
286288 return has_match ;
287289 }
290+
288291 // TODO other combinators
289- to_encapsulate . push ( { node, block } ) ;
290- return true ;
292+ return mark ( block , node ) ;
291293 }
292- to_encapsulate . push ( { node , block } ) ;
293- return true ;
294+
295+ return mark ( block , node ) ;
294296}
295297
296298const regex_backslash_and_following_character = / \\ ( .) / g;
@@ -815,6 +817,11 @@ class Block {
815817 this . selectors . push ( selector ) ;
816818 this . end = selector . end ;
817819 }
820+
821+ can_ignore ( ) {
822+ return this . global || this . host || this . root ;
823+ }
824+
818825 get global ( ) {
819826 return (
820827 this . selectors . length >= 1 &&
0 commit comments