@@ -11,6 +11,7 @@ import {
11
11
isAttributeNode ,
12
12
isClassLevelObject ,
13
13
isClassNode ,
14
+ isExternalBlock ,
14
15
isRootLevelObject ,
15
16
isRootNode ,
16
17
toAttrToken ,
@@ -61,41 +62,50 @@ export async function constructBlock(root: postcss.Root, block: Block, file: str
61
62
parsedSelectors . forEach ( ( iSel ) => {
62
63
63
64
let keySel = iSel . key ;
64
- let currentCompoundSel : CompoundSelector | undefined = iSel . selector ;
65
+ let sel : CompoundSelector | undefined = iSel . selector ;
65
66
66
67
// Assert this selector is well formed according to CSS Blocks' selector rules.
67
68
assertValidSelector ( block , rule , iSel , file ) ;
68
69
69
70
// For each `CompoundSelector` in this rule, configure the `Block` object
70
71
// depending on the BlockType.
71
- while ( currentCompoundSel ) {
72
-
73
- let isKey = ( keySel === currentCompoundSel ) ;
72
+ while ( sel ) {
73
+
74
+ let isKey = ( keySel === sel ) ;
74
75
let blockClass : BlockClass | undefined = undefined ;
75
76
let foundStyles : Style [ ] = [ ] ;
76
77
77
- for ( let node of currentCompoundSel . nodes ) {
78
- if ( isRootNode ( node ) ) {
78
+ // If this is an external Style, move on. These are validated
79
+ // in `assert-foreign-global-attribute`.
80
+ let blockName = sel . nodes . find ( n => n . type === selectorParser . TAG ) ;
81
+ if ( blockName ) {
82
+ sel = sel . next && sel . next . selector ;
83
+ continue ;
84
+ }
85
+
86
+ for ( let node of sel . nodes ) {
87
+ if ( isRootNode ( node ) ) {
79
88
blockClass = block . rootClass ;
80
89
}
81
- else if ( isClassNode ( node ) ) {
90
+ else if ( isClassNode ( node ) ) {
82
91
blockClass = block . ensureClass ( node . value ) ;
83
92
}
84
- else if ( isAttributeNode ( node ) ) {
85
- // The fact that a base class exists for all state selectors is validated elsewhere
93
+ else if ( isAttributeNode ( node ) ) {
94
+ // The fact that a base class exists for all state selectors is
95
+ // validated in `assertBlockObject`.
86
96
foundStyles . push ( blockClass ! . ensureAttributeValue ( toAttrToken ( node ) ) ) ;
87
97
}
88
-
89
98
}
90
- // If we haven't found any terminating states, we're targeting the discovered Block class.
91
- if ( blockClass && ! foundStyles . length ) { foundStyles . push ( blockClass ) ; }
92
99
93
- // If this is the key selector, save this ruleset on the created style.
94
- if ( isKey ) {
95
- foundStyles . map ( s => styleRuleTuples . add ( [ s , rule ] ) ) ;
96
- }
100
+ // If we haven't found any terminating states, we're targeting the discovered Block class.
101
+ if ( blockClass && ! foundStyles . length ) { foundStyles . push ( blockClass ) ; }
102
+
103
+ // If this is the key selector, save this ruleset on the created style.
104
+ if ( isKey ) {
105
+ foundStyles . map ( s => styleRuleTuples . add ( [ s , rule ] ) ) ;
106
+ }
97
107
98
- currentCompoundSel = currentCompoundSel . next && currentCompoundSel . next . selector ;
108
+ sel = sel . next && sel . next . selector ;
99
109
}
100
110
} ) ;
101
111
} ) ;
@@ -268,6 +278,23 @@ function assertBlockObject(block: Block, sel: CompoundSelector, rule: postcss.Ru
268
278
// Test each node in selector
269
279
let result = sel . nodes . reduce < NodeAndType | null > (
270
280
( found , n ) => {
281
+
282
+ // If this is an external Block reference, indicate we have encountered it.
283
+ // If this is not the first BlockType encountered, throw the appropriate error.
284
+ if ( n . type === selectorParser . TAG ) {
285
+ if ( found === null ) {
286
+ found = {
287
+ blockType : BlockType . block ,
288
+ node : n ,
289
+ } ;
290
+ } else {
291
+ throw new errors . InvalidBlockSyntax (
292
+ `External Block ${ n } must be the first selector in "${ rule . selector } "` ,
293
+ loc ( file , rule , sel . nodes [ 0 ] ) ,
294
+ ) ;
295
+ }
296
+ }
297
+
271
298
// If selecting the root element, indicate we have encountered it. If this
272
299
// is not the first BlockType encountered, throw the appropriate error
273
300
if ( isRootNode ( n ) ) {
@@ -282,11 +309,6 @@ function assertBlockObject(block: Block, sel: CompoundSelector, rule: postcss.Ru
282
309
`${ n } cannot be on the same element as ${ found . node } : ${ rule . selector } ` ,
283
310
loc ( file , rule , sel . nodes [ 0 ] ) ,
284
311
) ;
285
- } else if ( found . blockType === BlockType . attribute ) {
286
- throw new errors . InvalidBlockSyntax (
287
- `It's redundant to specify a state with the an explicit .root: ${ rule . selector } ` ,
288
- loc ( file , rule , n ) ,
289
- ) ;
290
312
}
291
313
}
292
314
}
@@ -308,7 +330,7 @@ function assertBlockObject(block: Block, sel: CompoundSelector, rule: postcss.Ru
308
330
) ;
309
331
} else if ( found . blockType === BlockType . class || found . blockType === BlockType . classAttribute ) {
310
332
found = { node : n , blockType : BlockType . classAttribute } ;
311
- } else if ( found . blockType === BlockType . root || found . blockType === BlockType . attribute ) {
333
+ } else if ( found . blockType === BlockType . block || found . blockType === BlockType . root || found . blockType === BlockType . attribute ) {
312
334
found = { node : n , blockType : BlockType . attribute } ;
313
335
}
314
336
}
@@ -351,6 +373,20 @@ function assertBlockObject(block: Block, sel: CompoundSelector, rule: postcss.Ru
351
373
loc ( file , rule , sel . nodes [ 0 ] ) ) ;
352
374
}
353
375
376
+ if ( isExternalBlock ( result ) ) {
377
+ let external = block . getReferencedBlock ( result . node . value ! ) ;
378
+ if ( ! external ) { throw new errors . InvalidBlockSyntax ( `` , loc ( file , rule , sel . nodes [ 0 ] ) ) ; }
379
+ let globalStates = external . rootClass . allAttributeValues ( ) . filter ( ( a ) => a . isGlobal ) ;
380
+ if ( ! globalStates . length ) {
381
+ throw new errors . InvalidBlockSyntax (
382
+ `External Block '${ result . node . value } ' has no global states.` ,
383
+ loc ( file , rule , sel . nodes [ 0 ] ) ) ;
384
+ }
385
+ throw new errors . InvalidBlockSyntax (
386
+ `Missing global state selector on external Block '${ result . node . value } '. Did you mean one of: ${ globalStates . map ( ( s ) => s . asSource ( ) ) . join ( " " ) } ` ,
387
+ loc ( file , rule , sel . nodes [ 0 ] ) ) ;
388
+ }
389
+
354
390
// Otherwise, return the block, type and associated node.
355
391
else {
356
392
return {
0 commit comments