1
1
import { postcss } from "opticss" ;
2
2
3
- import { Block } from "../BlockTree" ;
3
+ import { AttributeSelector , BlockExport , BlockReference , BlockSyntaxVersion , ClassSelector , Declaration , DefinitionAST , DefinitionRoot , ForeignAttributeSelector , GlobalDeclaration , LocalBlockExport , Mapper , Name , Rename , Rule , ScopeSelector , Selector , Visitor , builders , map as mapToDefinition , visit } from "../BlockParser/ast" ;
4
+ import { BLOCK_GLOBAL } from "../BlockSyntax" ;
5
+ import { Block , BlockClass , Style , isAttrValue , isBlockClass } from "../BlockTree" ;
4
6
import { ResolvedConfiguration } from "../configuration" ;
5
7
6
8
export const INLINE_DEFINITION_FILE = Symbol ( "Inline Definition" ) ;
7
9
8
- export type PathResolver = ( config : ResolvedConfiguration , fromBlock : Block , toBlock : Block , fromPath : string ) => string ;
10
+ export type PathResolver = ( block : Block , fromPath : string ) => string ;
9
11
10
- export const filesystemPathResolver : PathResolver = ( _config : ResolvedConfiguration , _fromBlock : Block , _toBlock : Block , fromPath : string ) : string => {
11
- return fromPath . replace ( ".block" , "" ) ;
12
- } ;
12
+ class CompiledDefinitionMapper implements Mapper < DefinitionAST > {
13
+ pathResolver : PathResolver ;
14
+ block : Block ;
15
+ constructor ( block : Block , pathResolver : PathResolver ) {
16
+ this . block = block ;
17
+ this . pathResolver = pathResolver ;
18
+ }
19
+ BlockExport ( fromPath : string , exports : Array < Name | Rename > ) {
20
+ fromPath = this . pathResolver ( this . block , fromPath ) ;
21
+ return builders . blockExport ( fromPath , exports ) ;
22
+ }
23
+ BlockReference ( fromPath : string , defaultName : string , references : Array < Name | Rename > ) {
24
+ fromPath = this . pathResolver ( this . block , fromPath ) ;
25
+ return builders . blockReference ( fromPath , defaultName , references ) ;
26
+ }
27
+ }
28
+
29
+ class SelectorASTBuilder implements Visitor < DefinitionAST > {
30
+ selector : string ;
31
+ constructor ( ) {
32
+ this . selector = "" ;
33
+ }
34
+ AttributeSelector ( attr : AttributeSelector ) : void {
35
+ this . selector += `[${ attr . attribute } ` ;
36
+ if ( attr . matches ) {
37
+ this . selector += attr . matches . matcher ;
38
+ this . selector += `"${ attr . matches . value } "` ;
39
+ }
40
+ this . selector += "]" ;
41
+ }
42
+ ForeignAttributeSelector ( attr : ForeignAttributeSelector ) : void {
43
+ this . selector += `[${ attr . ns } |${ attr . attribute } ` ;
44
+ if ( attr . matches ) {
45
+ this . selector += attr . matches . matcher ;
46
+ this . selector += `"${ attr . matches . value } "` ;
47
+ }
48
+ this . selector += "]" ;
49
+ }
50
+ ScopeSelector ( _scopeSelector : ScopeSelector ) : void {
51
+ this . selector += `:scope` ;
52
+ }
53
+ ClassSelector ( classSelector : ClassSelector ) : void {
54
+ this . selector += `.${ classSelector . name } ` ;
55
+ }
56
+ }
57
+
58
+ class CompiledDefinitionPostcssASTBuilder implements Visitor < DefinitionAST > {
59
+ postcss : typeof postcss ;
60
+ root : postcss . Root ;
61
+ currentRule : postcss . Rule | undefined ;
62
+ constructor ( postcssImpl : typeof postcss ) {
63
+ this . postcss = postcssImpl ;
64
+ this . root = this . postcss . root ( ) ;
65
+ }
66
+
67
+ namedReferences ( references : Array < Name | Rename > ) : string {
68
+ let result = "" ;
69
+ result += "(" ;
70
+ let prevRef = false ;
71
+ for ( let ref of references ) {
72
+ if ( prevRef ) {
73
+ result += ", " ;
74
+ }
75
+ result += ref . name ;
76
+ if ( ref . asName ) {
77
+ result += ` as ${ ref . asName } ` ;
78
+ }
79
+ prevRef = true ;
80
+ }
81
+ result += ")" ;
82
+ return result ;
83
+ }
84
+
85
+ BlockSyntaxVersion ( blockSyntaxVersion : BlockSyntaxVersion ) : void {
86
+ this . root . append ( this . postcss . atRule ( {
87
+ name : "block-syntax-version" ,
88
+ params : blockSyntaxVersion . version . toString ( ) ,
89
+ } ) ) ;
90
+ }
91
+
92
+ BlockReference ( blockReference : BlockReference ) : void {
93
+ let params = "" ;
94
+ if ( blockReference . defaultName ) {
95
+ params += blockReference . defaultName ;
96
+ }
97
+ if ( blockReference . defaultName && blockReference . references ) {
98
+ params += ", " ;
99
+ }
100
+ if ( blockReference . references ) {
101
+ params += this . namedReferences ( blockReference . references ) ;
102
+ }
103
+ params += ` from "${ blockReference . fromPath } "` ;
104
+ let atRule = this . postcss . atRule ( {
105
+ name : "block" ,
106
+ params,
107
+ } ) ;
108
+ this . root . append ( atRule ) ;
109
+ }
110
+
111
+ LocalBlockExport ( localBlockExport : LocalBlockExport ) : void {
112
+ let params = this . namedReferences ( localBlockExport . exports ) ;
113
+ let atRule = this . postcss . atRule ( {
114
+ name : "export" ,
115
+ params,
116
+ } ) ;
117
+ this . root . append ( atRule ) ;
118
+ }
119
+
120
+ BlockExport ( blockExport : BlockExport ) : void {
121
+ let params = this . namedReferences ( blockExport . exports ) ;
122
+ params += ` from "${ blockExport . fromPath } "` ;
123
+ let atRule = this . postcss . atRule ( {
124
+ name : "export" ,
125
+ params,
126
+ } ) ;
127
+ this . root . append ( atRule ) ;
128
+ }
129
+
130
+ Rule ( rule : Rule < DefinitionAST > ) : void | boolean {
131
+ let selectors = new Array < string > ( ) ;
132
+ for ( let sel of rule . selectors ) {
133
+ let visitor = new SelectorASTBuilder ( ) ;
134
+ visit ( visitor , sel ) ;
135
+ selectors . push ( visitor . selector ) ;
136
+ }
137
+ let currentRule = postcss . rule ( { selectors} ) ;
138
+ for ( let declaration of rule . declarations ) {
139
+ currentRule . append (
140
+ postcss . decl ( { prop : declaration . property , value : declaration . value } ) ,
141
+ ) ;
142
+ }
143
+ this . root . append ( currentRule ) ;
144
+ return false ;
145
+ }
146
+ GlobalDeclaration ( globalDeclaration : GlobalDeclaration ) : false {
147
+ let selectorBuilder = new SelectorASTBuilder ( ) ;
148
+ visit ( selectorBuilder , globalDeclaration . selector ) ;
149
+ let atRule = postcss . atRule ( { name : BLOCK_GLOBAL , params : selectorBuilder . selector } ) ;
150
+ this . root . append ( atRule ) ;
151
+ return false ;
152
+ }
153
+ }
13
154
14
155
export class BlockDefinitionCompiler {
15
156
postcss : typeof postcss ;
16
157
config : ResolvedConfiguration ;
17
- constructor ( postcssImpl : typeof postcss , config : ResolvedConfiguration ) {
158
+ pathResolver : PathResolver ;
159
+ constructor ( postcssImpl : typeof postcss , pathResolver : PathResolver , config : ResolvedConfiguration ) {
18
160
this . postcss = postcssImpl ;
19
161
this . config = config ;
162
+ this . pathResolver = pathResolver ;
163
+ }
164
+
165
+ compile ( block : Block , reservedClassNames : Set < string > ) : postcss . Root {
166
+ let ast = this . compileToAST ( block , reservedClassNames ) ;
167
+ let visitor = new CompiledDefinitionPostcssASTBuilder ( this . postcss ) ;
168
+ visit ( visitor , ast ) ;
169
+ return visitor . root ;
170
+ }
171
+
172
+ globalDeclarations ( block : Block ) : Array < GlobalDeclaration > {
173
+ let globals = new Array < GlobalDeclaration > ( ) ;
174
+ for ( let attrValue of block . rootClass . allAttributeValues ( ) ) {
175
+ if ( attrValue . isGlobal ) {
176
+ let selector = builders . attributeSelector ( attrValue . attribute . name , attrValue . isPresenceRule ? undefined : attrValue . value ) ;
177
+ globals . push ( builders . globalDeclaration ( selector ) ) ;
178
+ }
179
+ }
180
+ return globals ;
181
+ }
182
+
183
+ compileToAST ( block : Block , reservedClassNames : Set < string > ) : DefinitionRoot {
184
+ if ( ! block . blockAST ) {
185
+ throw new Error ( "The block's AST is missing." ) ;
186
+ }
187
+ let mapper = new CompiledDefinitionMapper ( block , this . pathResolver ) ;
188
+ let definitionRoot : DefinitionRoot = mapToDefinition ( mapper , block . blockAST , "block" , "definition" ) ;
189
+ definitionRoot . children . unshift ( ...this . globalDeclarations ( block ) ) ;
190
+ definitionRoot . children . unshift ( builders . blockSyntaxVersion ( 1 ) ) ;
191
+ for ( let style of block . all ( true ) ) {
192
+ definitionRoot . children . push ( this . styleToRule ( style , reservedClassNames ) ) ;
193
+ }
194
+ return definitionRoot ;
20
195
}
21
196
22
- compile ( block : Block , root : postcss . Root , _pathResolver : PathResolver ) : postcss . Root {
23
- this . blockReferences ( root , block ) ;
24
- return root ;
197
+ styleToRule ( style : Style , reservedClassNames : Set < string > ) : Rule < DefinitionAST > {
198
+ let selectors = new Array < Selector < DefinitionAST > > ( ) ;
199
+ let blockClass : BlockClass = isAttrValue ( style ) ? style . blockClass : style ;
200
+ let elementSelector : ClassSelector | ScopeSelector ;
201
+ if ( blockClass . isRoot ) {
202
+ elementSelector = builders . scopeSelector ( ) ;
203
+ } else {
204
+ elementSelector = builders . classSelector ( blockClass . name ) ;
205
+ }
206
+ if ( isAttrValue ( style ) ) {
207
+ let attributeSelector : AttributeSelector ;
208
+ if ( style . isPresenceRule ) {
209
+ attributeSelector = builders . attributeSelector ( style . attribute . name ) ;
210
+ } else {
211
+ attributeSelector = builders . attributeSelector ( style . attribute . name , style . value ) ;
212
+ }
213
+ selectors . push ( builders . keyCompoundSelector ( elementSelector , [ attributeSelector ] ) ) ;
214
+ } else {
215
+ selectors . push ( elementSelector ) ;
216
+ }
217
+ let declarations = new Array < Declaration > ( ) ;
218
+ if ( isBlockClass ( style ) && style . isRoot ) {
219
+ declarations . push ( builders . declaration ( "block-id" , `"${ style . block . guid } "` ) ) ;
220
+ declarations . push ( builders . declaration ( "block-name" , `"${ style . block . name } "` ) ) ;
221
+ }
222
+ declarations . push ( builders . declaration ( "block-class" , style . cssClass ( this . config , reservedClassNames ) ) ) ;
223
+ declarations . push ( builders . declaration ( "block-interface-index" , style . index . toString ( ) ) ) ;
224
+ let aliasValues = new Array ( ...style . getStyleAliases ( ) ) ;
225
+ if ( aliasValues . length ) {
226
+ declarations . push ( builders . declaration ( "block-alias" , aliasValues . join ( " " ) ) ) ;
227
+ }
228
+ return builders . rule ( selectors , declarations ) ;
25
229
}
26
230
27
231
blockReferences ( root : postcss . Root , block : Block ) : void {
@@ -30,9 +234,12 @@ export class BlockDefinitionCompiler {
30
234
} ) ;
31
235
}
32
236
33
- insertReference ( _css : postcss . Root , _definitionPath : string ) {
34
- throw new Error ( "Method not implemented." ) ;
237
+ insertReference ( css : postcss . Root , definitionPath : string ) {
238
+ let comment = this . postcss . comment ( { text : `#blockDefinitionURL=${ definitionPath } ` } ) ;
239
+ Object . assign ( comment . raws , { left : "" , right : "" } ) ;
240
+ css . append ( comment ) ;
35
241
}
242
+
36
243
insertInlineReference ( _css : postcss . Root , _definition : postcss . Root ) {
37
244
throw new Error ( "Method not implemented." ) ;
38
245
}
0 commit comments