@@ -12,10 +12,7 @@ import {
12
12
TemplateIntegrationOptions ,
13
13
TemplateTypes ,
14
14
} from "@opticss/template-api" ;
15
- import {
16
- ObjectDictionary ,
17
- objectValues ,
18
- } from "@opticss/util" ;
15
+ import { ObjectDictionary , objectValues } from "@opticss/util" ;
19
16
import { IdentGenerator } from "opticss" ;
20
17
21
18
import { BlockFactory } from "../BlockParser" ;
@@ -28,7 +25,7 @@ import { TemplateValidator, TemplateValidatorOptions } from "./validations";
28
25
29
26
/**
30
27
* This interface defines a JSON friendly serialization
31
- * of a {TemplateAnalysis }.
28
+ * of an {Analysis }.
32
29
*/
33
30
export interface SerializedAnalysis {
34
31
template : SerializedTemplateInfo < keyof TemplateTypes > ;
@@ -39,8 +36,8 @@ export interface SerializedAnalysis {
39
36
}
40
37
41
38
/**
42
- * A TemplateAnalysis performs book keeping and ensures internal consistency of the block objects referenced
43
- * within a template. It is designed to be used as part of an AST walk over a template.
39
+ * An Analysis performs book keeping and ensures internal consistency of the block objects referenced
40
+ * within a single template. It is designed to be used as part of an AST walk over a template.
44
41
*
45
42
* 1. Call [[startElement startElement()]] at the beginning of an new html element.
46
43
* 2. Call [[addStyle addStyle(style, isDynamic)]] for all the styles used on the current html element.
@@ -93,22 +90,39 @@ export class Analysis<K extends keyof TemplateTypes> {
93
90
94
91
/**
95
92
* Return the number of blocks discovered in this Template.
96
- */
93
+ */
97
94
blockCount ( ) : number { return Object . keys ( this . blocks ) . length ; }
98
95
99
96
/**
100
97
* Convenience setter for adding a block to the template scope.
101
98
*/
102
- addBlock ( name : string , block : Block ) { this . blocks [ name ] = block ; }
99
+ addBlock ( name : string , block : Block ) : void { this . blocks [ name ] = block ; }
103
100
104
101
/**
105
102
* Convenience getter for fetching a block from the template scope.
106
103
*/
107
- getBlock ( name : string ) : Block { return this . blocks [ name ] ; }
104
+ getBlock ( name : string ) : Block | undefined { return this . blocks [ name ] ; }
105
+
106
+ /**
107
+ * Return the number of elements discovered in this Analysis.
108
+ */
109
+ elementCount ( ) : number { return this . elements . size ; }
110
+
111
+ /**
112
+ * Get the nth element discovered in this Analysis.
113
+ */
114
+ getElement < BooleanExpression , StringExpression , TernaryExpression > ( idx : number ) : ElementAnalysis < BooleanExpression , StringExpression , TernaryExpression > {
115
+ let mapIter = this . elements . entries ( ) ;
116
+ let el = mapIter . next ( ) . value ;
117
+ for ( let i = 0 ; i < idx ; i ++ ) {
118
+ el = mapIter . next ( ) . value ;
119
+ }
120
+ return el [ 1 ] ;
121
+ }
108
122
109
123
/**
110
124
* Return the number of styles discovered in this Analysis' Template.
111
- * This is slow.
125
+ * TODO: This is slow. We can pre-compute this as elements are added .
112
126
*/
113
127
styleCount ( ) : number {
114
128
let c = 0 ;
@@ -124,25 +138,11 @@ export class Analysis<K extends keyof TemplateTypes> {
124
138
}
125
139
126
140
/**
127
- * Return the number of elements discovered in this Analysis.
128
- */
129
- elementCount ( ) : number { return this . elements . size ; }
130
-
131
- /**
132
- * Get the nth element discovered in this Analysis.
141
+ * Get either the owner Analyzer's optimization options, or a default set of options.
133
142
*/
134
- getElement < BooleanExpression , StringExpression , TernaryExpression > ( idx : number ) : ElementAnalysis < BooleanExpression , StringExpression , TernaryExpression > {
135
- let mapIter = this . elements . entries ( ) ;
136
- let el = mapIter . next ( ) . value ;
137
- for ( let i = 0 ; i < idx ; i ++ ) {
138
- el = mapIter . next ( ) . value ;
139
- }
140
- return el [ 1 ] ;
141
- }
142
-
143
143
optimizationOptions ( ) : TemplateIntegrationOptions {
144
- // TODO: take this as an argument from the template integration .
145
- return {
144
+ // TODO: Optimization options must be handled / propagated better .
145
+ return this . parent ? this . parent . optimizationOptions : {
146
146
rewriteIdents : {
147
147
id : false ,
148
148
class : true ,
@@ -164,6 +164,7 @@ export class Analysis<K extends keyof TemplateTypes> {
164
164
}
165
165
166
166
/**
167
+ * Returns the local name of the provided in this Analysis' template.
167
168
* @param block The block for which the local name should be returned.
168
169
* @return The local name of the given block.
169
170
*/
@@ -184,46 +185,42 @@ export class Analysis<K extends keyof TemplateTypes> {
184
185
return null ;
185
186
}
186
187
188
+ /**
189
+ * Get a new {ElementAnalysis} object to track an individual element's {Style}
190
+ * consumption in this {Analysis}' template. Every {ElementAnalysis} returned from
191
+ * `Analysis.startElement` must be passed to `Analysis.endElement` before startElement
192
+ * is called again.
193
+ * @param location The {SourceLocation} of this element in the template.
194
+ * @param tagName Optional. The tag name of the element being represented by this {ElementAnalysis}.
195
+ * @returns A new {ElementAnalysis}.
196
+ */
187
197
startElement < BooleanExpression , StringExpression , TernaryExpression > ( location : SourceLocation | SourcePosition , tagName ?: string ) : ElementAnalysis < BooleanExpression , StringExpression , TernaryExpression > {
188
- if ( isSourcePosition ( location ) ) {
189
- location = { start : location } ;
190
- }
198
+ if ( isSourcePosition ( location ) ) { location = { start : location } ; }
191
199
if ( this . currentElement ) {
192
200
throw new Error ( "Internal Error: failure to call endElement before previous call to startElement." ) ;
193
201
}
194
202
this . currentElement = new ElementAnalysis < BooleanExpression , StringExpression , TernaryExpression > ( location , tagName , this . idGenerator . nextIdent ( ) ) ;
195
203
return this . currentElement ;
196
204
}
197
205
198
- endElement < BooleanExpression , StringExpression , TernaryExpression > ( element : ElementAnalysis < BooleanExpression , StringExpression , TernaryExpression > , endPosition ?: SourcePosition ) {
206
+ /**
207
+ * Finish an {ElementAnalysis} object returned from `Analysis.startElement` to
208
+ * the end location in source and save {Style} usage on the parent {Analysis}.
209
+ * @param element The {ElementAnalysis} we are finishing.
210
+ * @param endPosition Optional. The location in code where this element tag is closed..
211
+ */
212
+ endElement < BooleanExpression , StringExpression , TernaryExpression > ( element : ElementAnalysis < BooleanExpression , StringExpression , TernaryExpression > , endPosition ?: SourcePosition ) : void {
199
213
if ( this . currentElement !== element ) {
200
214
throw new Error ( "Element is not the current element." ) ;
201
215
}
202
- if ( endPosition ) {
203
- element . sourceLocation . end = endPosition ;
204
- }
205
- this . addElement ( element ) ;
206
- this . currentElement = undefined ;
207
- }
208
-
209
- private addFilename ( pos : SourcePosition | undefined ) {
210
- if ( pos && ! pos . filename ) {
211
- pos . filename = this . template . identifier ;
212
- }
213
- }
214
-
215
- addElement < BooleanExpression , StringExpression , TernaryExpression > ( element : ElementAnalysis < BooleanExpression , StringExpression , TernaryExpression > ) {
216
- if ( ! element . id ) {
217
- element . id = this . idGenerator . nextIdent ( ) ;
218
- }
216
+ if ( endPosition ) { element . sourceLocation . end = endPosition ; }
217
+ if ( ! element . id ) { element . id = this . idGenerator . nextIdent ( ) ; }
219
218
if ( this . elements . get ( element . id ) ) {
220
219
throw new Error ( `Internal Error: an element with id = ${ element . id } already exists in this analysis` ) ;
221
220
}
222
- this . addFilename ( element . sourceLocation . start ) ;
223
- this . addFilename ( element . sourceLocation . end ) ;
224
- if ( ! element . sealed ) {
225
- element . seal ( ) ;
226
- }
221
+ this . ensureFilename ( element . sourceLocation . start ) ;
222
+ this . ensureFilename ( element . sourceLocation . end ) ;
223
+ if ( ! element . sealed ) { element . seal ( ) ; }
227
224
this . validator . validate ( this , element ) ;
228
225
this . elements . set ( element . id , element ) ;
229
226
if ( this . parent ) {
@@ -234,6 +231,18 @@ export class Analysis<K extends keyof TemplateTypes> {
234
231
this . parent . saveDynamicStyle ( s , this ) ;
235
232
}
236
233
}
234
+ this . currentElement = undefined ;
235
+ }
236
+
237
+ /**
238
+ * Given a {SourcePosition}, ensure that the file name is present. If not,
239
+ * add the template identifier.
240
+ * @param post The {SourcePosition} we are validating.
241
+ */
242
+ private ensureFilename ( pos : SourcePosition | undefined ) {
243
+ if ( pos && ! pos . filename ) {
244
+ pos . filename = this . template . identifier ;
245
+ }
237
246
}
238
247
239
248
/**
0 commit comments