1
1
import { BlockPathError , ErrorLocation } from '../errors' ;
2
2
import { CLASS_NAME_IDENT as CSS_IDENT } from "./blockSyntax" ;
3
+ import { StateInfo } from "../BlockParser" ;
3
4
4
- export interface BlockToken {
5
+ interface BlockToken {
5
6
type : 'block' ;
6
7
name : string ;
7
8
}
8
9
9
- export interface ClassToken {
10
+ interface ClassToken {
10
11
type : 'class' ;
11
12
name : string ;
12
13
}
@@ -18,20 +19,23 @@ export interface StateToken {
18
19
value ?: string ;
19
20
}
20
21
21
- export type Token = BlockToken | ClassToken | StateToken ;
22
+ type Token = BlockToken | ClassToken | StateToken ;
22
23
23
- export const isBlock = ( token ?: Token ) : token is BlockToken => ! ! token && token . type === 'block' ;
24
- export const isClass = ( token ?: Token ) : token is ClassToken => ! ! token && token . type === 'class' ;
25
- export const isState = ( token ?: Token ) : token is StateToken => ! ! token && token . type === 'state' ;
26
- export const hasName = ( token ?: Token ) : boolean => ! ! token && ! ! token . name ;
27
- export const hasNamespace = ( token ?: Token ) : boolean => isState ( token ) && ! ! token . namespace ;
28
- export const hasValue = ( token ?: Token ) : boolean => isState ( token ) && ! ! token . value ;
24
+ const isBlock = ( token ?: Token ) : token is BlockToken => ! ! token && token . type === 'block' ;
25
+ const isClass = ( token ?: Token ) : token is ClassToken => ! ! token && token . type === 'class' ;
26
+ const isState = ( token ?: Token ) : token is StateToken => ! ! token && token . type === 'state' ;
27
+ const hasName = ( token ?: Token ) : boolean => ! ! token && ! ! token . name ;
28
+ const hasNamespace = ( token ?: Token ) : boolean => isState ( token ) && ! ! token . namespace ;
29
29
30
30
const STATE_BEGIN = "[" ;
31
31
const STATE_END = "]" ;
32
32
const CLASS_BEGIN = "." ;
33
33
const NAMESPACE_END = "|" ;
34
34
const VALUE_START = "=" ;
35
+ const SINGLE_QUOTE = `'` ;
36
+ const DOUBLE_QUOTE = `"` ;
37
+ const WHITESPACE_REGEXP = / \s / g;
38
+ const SEPARATORS = new Set ( [ CLASS_BEGIN , STATE_BEGIN ] ) ;
35
39
36
40
export const ERRORS = {
37
41
whitespace : "Whitespace is only allowed in quoted state values" ,
@@ -56,8 +60,6 @@ function stringify(tokens: Token[]): string {
56
60
return out ;
57
61
}
58
62
59
- const SEPARATORS = new Set ( [ "." , "[" ] ) ;
60
-
61
63
/**
62
64
* Simple utility to easily walk over string data one character at a time.
63
65
*/
@@ -99,7 +101,7 @@ export class BlockPath {
99
101
private _class : ClassToken ;
100
102
private _state : StateToken ;
101
103
102
- protected tokens : Token [ ] = [ ] ;
104
+ private tokens : Token [ ] = [ ] ;
103
105
104
106
/**
105
107
* Throw a new BlockPathError with the given message.
@@ -147,10 +149,10 @@ export class BlockPath {
147
149
148
150
while ( char = walker . next ( ) ) {
149
151
150
- switch ( char ) {
152
+ switch ( true ) {
151
153
152
154
// If a period, we've finished the previous token and are now building a class name.
153
- case CLASS_BEGIN :
155
+ case char === CLASS_BEGIN :
154
156
if ( isState ( token ) ) { this . throw ( ERRORS . illegalCharInState ( char ) ) ; }
155
157
token . name = working ;
156
158
this . addToken ( token ) ;
@@ -159,7 +161,7 @@ export class BlockPath {
159
161
break ;
160
162
161
163
// If the beginning of a state, we've finished the previous token and are now building a state.
162
- case STATE_BEGIN :
164
+ case char === STATE_BEGIN :
163
165
if ( isState ( token ) ) { this . throw ( ERRORS . illegalCharInState ( char ) ) ; }
164
166
token . name = working ;
165
167
this . addToken ( token ) ;
@@ -168,37 +170,37 @@ export class BlockPath {
168
170
break ;
169
171
170
172
// If the end of a state, set the state part we've been working on and finish.
171
- case STATE_END :
173
+ case char === STATE_END :
172
174
if ( ! isState ( token ) ) { return this . throw ( ERRORS . illegalCharNotInState ( char ) ) ; }
173
175
token . name ? ( token . value = working ) : ( token . name = working ) ;
174
176
this . addToken ( token ) ;
175
177
working = "" ;
176
178
177
- // The character immediately following a `STATE_END` *must* be another `SEPERATOR `
179
+ // The character immediately following a `STATE_END` *must* be another `SEPARATORS `
178
180
// Depending on the next value, seed our token input
179
181
let next = walker . next ( ) ;
180
182
if ( next && ! SEPARATORS . has ( next ) ) { this . throw ( ERRORS . expectsSepInsteadRec ( next ) ) ; }
181
183
token = ( next === STATE_BEGIN ) ? { type : 'state' , namespace : '' , name : '' } : { type : 'class' , name : '' } ;
182
184
break ;
183
185
184
186
// When we find a namespace terminator, set the namespace property of the state token we're working on.
185
- case NAMESPACE_END :
187
+ case char === NAMESPACE_END :
186
188
if ( ! isState ( token ) ) { return this . throw ( ERRORS . illegalCharNotInState ( char ) ) ; }
187
189
token . namespace = working ;
188
190
working = "" ;
189
191
break ;
190
192
191
193
// If the start of the value section of a state part, set the name we've been working on and move on.
192
- case VALUE_START :
194
+ case char === VALUE_START :
193
195
if ( ! isState ( token ) ) { this . throw ( ERRORS . illegalCharNotInState ( char ) ) ; }
194
196
if ( ! working ) { this . throw ( ERRORS . noname ) ; }
195
197
token . name = working ;
196
198
working = "" ;
197
199
break ;
198
200
199
201
// If the opening quote of the value section of a state part, greedily consume everything between quotes.
200
- case `"` :
201
- case `'` :
202
+ case char === SINGLE_QUOTE :
203
+ case char === DOUBLE_QUOTE :
202
204
if ( ! isState ( token ) ) { return this . throw ( ERRORS . illegalCharNotInState ( char ) ) ; }
203
205
working = walker . consume ( char ) ;
204
206
if ( walker . peek ( ) !== char ) { this . throw ( ERRORS . mismatchedQuote ) ; }
@@ -208,7 +210,7 @@ export class BlockPath {
208
210
// We should never encounter whitespace in this switch statement.
209
211
// The only place whitespace is allowed is between quotes, which
210
212
// is handled above.
211
- case ` ` :
213
+ case WHITESPACE_REGEXP . test ( char ) :
212
214
this . throw ( ERRORS . whitespace ) ;
213
215
214
216
// If none of the above special characters, add this character to our working string.
@@ -239,45 +241,51 @@ export class BlockPath {
239
241
* @param path The BlockPath input data.
240
242
* @param location An optional ErrorLocation object for more detailed error reporting.
241
243
*/
242
- constructor ( path : string | BlockPath | Token [ ] , location ?: ErrorLocation ) {
244
+ constructor ( path : string | BlockPath , location ?: ErrorLocation ) {
243
245
this . _location = location ;
244
246
if ( path instanceof BlockPath ) {
245
247
this . tokens = path . tokens ;
246
248
}
247
- else if ( path instanceof Array ) {
248
- this . tokens = path ;
249
- }
250
249
else {
251
250
this . tokenize ( path ) ;
252
251
}
253
252
}
254
253
255
- /**
256
- * Get the parsed block name of this Block Path
257
- */
258
- get block ( ) : string {
259
- return this . _block . name ;
254
+ private static from ( tokens : Token [ ] ) {
255
+ let path = new BlockPath ( '' ) ;
256
+ path . tokens = tokens ;
257
+ return path ;
260
258
}
261
259
262
260
/**
263
261
* Get the parsed Style path of this Block Path
264
262
*/
265
263
get path ( ) : string {
266
- return stringify ( this . tokens . slice ( 1 ) ) || '.root' ;
264
+ return stringify ( this . tokens . slice ( 1 ) ) ;
265
+ }
266
+
267
+ /**
268
+ * Get the parsed block name of this Block Path
269
+ */
270
+ get block ( ) : string {
271
+ return this . _block ? this . _block . name : "" ;
267
272
}
268
273
269
274
/**
270
275
* Get the parsed class name of this Block Path
271
276
*/
272
277
get class ( ) : string {
273
- return this . _class && this . _class . name || ' root' ;
278
+ return this . _class && this . _class . name || " root" ;
274
279
}
275
280
276
281
/**
277
- * Get the parsed state name of this Block Path
282
+ * Get the parsed state name of this Block Path and return the `StateInfo`
278
283
*/
279
- get state ( ) : StateToken | undefined {
280
- return this . _state ;
284
+ get state ( ) : StateInfo | undefined {
285
+ return {
286
+ group : this . _state . value ? this . _state . name : undefined ,
287
+ name : this . _state . value || this . _state . name ,
288
+ } ;
281
289
}
282
290
283
291
/**
@@ -291,14 +299,14 @@ export class BlockPath {
291
299
* Return a new BlockPath without the parent-most token.
292
300
*/
293
301
childPath ( ) {
294
- return new BlockPath ( this . tokens . slice ( 1 ) ) ;
302
+ return BlockPath . from ( this . tokens . slice ( 1 ) ) ;
295
303
}
296
304
297
305
/**
298
306
* Return a new BlockPath without the child-most token.
299
307
*/
300
308
parentPath ( ) {
301
- return new BlockPath ( this . tokens . slice ( 0 , - 1 ) ) ;
309
+ return BlockPath . from ( this . tokens . slice ( 0 , - 1 ) ) ;
302
310
}
303
311
304
312
}
0 commit comments