@@ -78,80 +78,89 @@ function inPath(node, ancestor, path, fromPathIndex) {
78
78
* A WeakMap for holding cached matcher functions for selectors.
79
79
* @type {WeakMap<SelectorAST, SelectorMatcher> }
80
80
*/
81
- const MATCHER_CACHE = new WeakMap ( ) ;
81
+ const MATCHER_CACHE = typeof WeakMap === 'function' ? new WeakMap : null ;
82
82
83
83
/**
84
- * Create and cache a matcher function for `selector`,
85
- * or return a cached matcher if one already exists.
84
+ * Look up a matcher function for `selector` in the cache.
85
+ * If it does not exist, generate it with `generateMatcher` and add it to the cache.
86
+ * In engines without WeakMap, the caching is skipped and matchers are generated with every call.
86
87
* @param {?SelectorAST } selector
87
88
* @returns {SelectorMatcher }
88
89
*/
89
90
function getMatcher ( selector ) {
90
- if ( ! selector ) {
91
+ if ( selector == null ) {
91
92
return ( ) => true ;
92
93
}
93
94
94
- let matcher = MATCHER_CACHE . get ( selector ) ;
95
- if ( matcher ) {
95
+ if ( MATCHER_CACHE != null ) {
96
+ let matcher = MATCHER_CACHE . get ( selector ) ;
97
+ if ( matcher != null ) {
98
+ return matcher ;
99
+ }
100
+ matcher = generateMatcher ( selector ) ;
101
+ MATCHER_CACHE . set ( selector , matcher ) ;
96
102
return matcher ;
97
103
}
98
104
105
+ return generateMatcher ( selector ) ;
106
+ }
107
+
108
+ /**
109
+ * Create a matcher function for `selector`,
110
+ * @param {?SelectorAST } selector
111
+ * @returns {SelectorMatcher }
112
+ */
113
+ function generateMatcher ( selector ) {
99
114
switch ( selector . type ) {
100
115
case 'wildcard' :
101
- matcher = ( ) => true ;
102
- break ;
116
+ return ( ) => true ;
103
117
104
118
case 'identifier' : {
105
119
const value = selector . value . toLowerCase ( ) ;
106
- matcher = ( node ) => value === node . type . toLowerCase ( ) ;
107
- break ;
120
+ return ( node ) => value === node . type . toLowerCase ( ) ;
108
121
}
109
122
110
123
case 'field' : {
111
124
const path = selector . name . split ( '.' ) ;
112
- matcher = ( node , ancestry ) => {
125
+ return ( node , ancestry ) => {
113
126
const ancestor = ancestry [ path . length - 1 ] ;
114
127
return inPath ( node , ancestor , path , 0 ) ;
115
128
} ;
116
- break ;
117
129
}
118
130
119
131
case 'matches' : {
120
132
const matchers = selector . selectors . map ( getMatcher ) ;
121
- matcher = ( node , ancestry , options ) => {
133
+ return ( node , ancestry , options ) => {
122
134
for ( let i = 0 ; i < matchers . length ; ++ i ) {
123
135
if ( matchers [ i ] ( node , ancestry , options ) ) { return true ; }
124
136
}
125
137
return false ;
126
138
} ;
127
- break ;
128
139
}
129
140
130
141
case 'compound' : {
131
142
const matchers = selector . selectors . map ( getMatcher ) ;
132
- matcher = ( node , ancestry , options ) => {
143
+ return ( node , ancestry , options ) => {
133
144
for ( let i = 0 ; i < matchers . length ; ++ i ) {
134
145
if ( ! matchers [ i ] ( node , ancestry , options ) ) { return false ; }
135
146
}
136
147
return true ;
137
148
} ;
138
- break ;
139
149
}
140
150
141
151
case 'not' : {
142
152
const matchers = selector . selectors . map ( getMatcher ) ;
143
- matcher = ( node , ancestry , options ) => {
153
+ return ( node , ancestry , options ) => {
144
154
for ( let i = 0 ; i < matchers . length ; ++ i ) {
145
155
if ( matchers [ i ] ( node , ancestry , options ) ) { return false ; }
146
156
}
147
157
return true ;
148
158
} ;
149
- break ;
150
159
}
151
160
152
161
case 'has' : {
153
162
const matchers = selector . selectors . map ( getMatcher ) ;
154
- matcher = ( node , ancestry , options ) => {
163
+ return ( node , ancestry , options ) => {
155
164
let result = false ;
156
165
157
166
const a = [ ] ;
@@ -174,25 +183,23 @@ function getMatcher(selector) {
174
183
175
184
return result ;
176
185
} ;
177
- break ;
178
186
}
179
187
180
188
case 'child' : {
181
189
const left = getMatcher ( selector . left ) ;
182
190
const right = getMatcher ( selector . right ) ;
183
- matcher = ( node , ancestry , options ) => {
191
+ return ( node , ancestry , options ) => {
184
192
if ( right ( node , ancestry , options ) ) {
185
193
return left ( ancestry [ 0 ] , ancestry . slice ( 1 ) , options ) ;
186
194
}
187
195
return false ;
188
196
} ;
189
- break ;
190
197
}
191
198
192
199
case 'descendant' : {
193
200
const left = getMatcher ( selector . left ) ;
194
201
const right = getMatcher ( selector . right ) ;
195
- matcher = ( node , ancestry , options ) => {
202
+ return ( node , ancestry , options ) => {
196
203
if ( right ( node , ancestry , options ) ) {
197
204
for ( let i = 0 , l = ancestry . length ; i < l ; ++ i ) {
198
205
if ( left ( ancestry [ i ] , ancestry . slice ( i + 1 ) , options ) ) {
@@ -202,119 +209,93 @@ function getMatcher(selector) {
202
209
}
203
210
return false ;
204
211
} ;
205
- break ;
206
212
}
207
213
208
214
case 'attribute' : {
209
215
const path = selector . name . split ( '.' ) ;
210
216
switch ( selector . operator ) {
211
217
case void 0 :
212
- matcher = ( node ) => getPath ( node , path ) != null ;
213
- break ;
218
+ return ( node ) => getPath ( node , path ) != null ;
214
219
case '=' :
215
220
switch ( selector . value . type ) {
216
221
case 'regexp' :
217
- matcher = ( node ) => {
222
+ return ( node ) => {
218
223
const p = getPath ( node , path ) ;
219
224
return typeof p === 'string' && selector . value . value . test ( p ) ;
220
225
} ;
221
- break ;
222
226
case 'literal' : {
223
227
const literal = `${ selector . value . value } ` ;
224
- matcher = ( node ) => literal === `${ getPath ( node , path ) } ` ;
225
- break ;
228
+ return ( node ) => literal === `${ getPath ( node , path ) } ` ;
226
229
}
227
230
case 'type' :
228
- matcher = ( node ) => selector . value . value === typeof getPath ( node , path ) ;
229
- break ;
230
- default :
231
- throw new Error ( `Unknown selector value type: ${ selector . value . type } ` ) ;
231
+ return ( node ) => selector . value . value === typeof getPath ( node , path ) ;
232
232
}
233
- break ;
233
+ throw new Error ( `Unknown selector value type: ${ selector . value . type } ` ) ;
234
234
case '!=' :
235
235
switch ( selector . value . type ) {
236
236
case 'regexp' :
237
- matcher = ( node ) => ! selector . value . value . test ( getPath ( node , path ) ) ;
238
- break ;
237
+ return ( node ) => ! selector . value . value . test ( getPath ( node , path ) ) ;
239
238
case 'literal' : {
240
239
const literal = `${ selector . value . value } ` ;
241
- matcher = ( node ) => literal !== `${ getPath ( node , path ) } ` ;
242
- break ;
240
+ return ( node ) => literal !== `${ getPath ( node , path ) } ` ;
243
241
}
244
242
case 'type' :
245
- matcher = ( node ) => selector . value . value !== typeof getPath ( node , path ) ;
246
- break ;
247
- default :
248
- throw new Error ( `Unknown selector value type: ${ selector . value . type } ` ) ;
243
+ return ( node ) => selector . value . value !== typeof getPath ( node , path ) ;
249
244
}
250
- break ;
245
+ throw new Error ( `Unknown selector value type: ${ selector . value . type } ` ) ;
251
246
case '<=' :
252
- matcher = ( node ) => getPath ( node , path ) <= selector . value . value ;
253
- break ;
247
+ return ( node ) => getPath ( node , path ) <= selector . value . value ;
254
248
case '<' :
255
- matcher = ( node ) => getPath ( node , path ) < selector . value . value ;
256
- break ;
249
+ return ( node ) => getPath ( node , path ) < selector . value . value ;
257
250
case '>' :
258
- matcher = ( node ) => getPath ( node , path ) > selector . value . value ;
259
- break ;
251
+ return ( node ) => getPath ( node , path ) > selector . value . value ;
260
252
case '>=' :
261
- matcher = ( node ) => getPath ( node , path ) >= selector . value . value ;
262
- break ;
263
- default :
264
- throw new Error ( `Unknown operator: ${ selector . operator } ` ) ;
253
+ return ( node ) => getPath ( node , path ) >= selector . value . value ;
265
254
}
266
- break ;
255
+ throw new Error ( `Unknown operator: ${ selector . operator } ` ) ;
267
256
}
268
257
269
258
case 'sibling' : {
270
259
const left = getMatcher ( selector . left ) ;
271
260
const right = getMatcher ( selector . right ) ;
272
- matcher = ( node , ancestry , options ) => {
273
- return right ( node , ancestry , options ) &&
261
+ return ( node , ancestry , options ) =>
262
+ right ( node , ancestry , options ) &&
274
263
sibling ( node , left , ancestry , LEFT_SIDE , options ) ||
275
264
selector . left . subject &&
276
265
left ( node , ancestry , options ) &&
277
266
sibling ( node , right , ancestry , RIGHT_SIDE , options ) ;
278
- } ;
279
- break ;
280
267
}
281
268
282
269
case 'adjacent' : {
283
270
const left = getMatcher ( selector . left ) ;
284
271
const right = getMatcher ( selector . right ) ;
285
- matcher = ( node , ancestry , options ) => {
286
- return right ( node , ancestry , options ) &&
272
+ return ( node , ancestry , options ) =>
273
+ right ( node , ancestry , options ) &&
287
274
adjacent ( node , left , ancestry , LEFT_SIDE , options ) ||
288
275
selector . right . subject &&
289
276
left ( node , ancestry , options ) &&
290
277
adjacent ( node , right , ancestry , RIGHT_SIDE , options ) ;
291
- } ;
292
- break ;
293
278
}
294
279
295
280
case 'nth-child' : {
296
281
const nth = selector . index . value ;
297
282
const right = getMatcher ( selector . right ) ;
298
- matcher = ( node , ancestry , options ) => {
299
- return right ( node , ancestry , options ) &&
283
+ return ( node , ancestry , options ) =>
284
+ right ( node , ancestry , options ) &&
300
285
nthChild ( node , ancestry , nth , options ) ;
301
- } ;
302
- break ;
303
286
}
304
287
305
288
case 'nth-last-child' : {
306
289
const nth = - selector . index . value ;
307
290
const right = getMatcher ( selector . right ) ;
308
- matcher = ( node , ancestry , options ) => {
309
- return right ( node , ancestry , options ) &&
291
+ return ( node , ancestry , options ) =>
292
+ right ( node , ancestry , options ) &&
310
293
nthChild ( node , ancestry , nth , options ) ;
311
- } ;
312
- break ;
313
294
}
314
295
315
296
case 'class' : {
316
297
const name = selector . name . toLowerCase ( ) ;
317
- matcher = ( node , ancestry ) => {
298
+ return ( node , ancestry ) => {
318
299
switch ( name ) {
319
300
case 'statement' :
320
301
if ( node . type . slice ( - 9 ) === 'Statement' ) return true ;
@@ -339,15 +320,10 @@ function getMatcher(selector) {
339
320
}
340
321
throw new Error ( `Unknown class name: ${ selector . name } ` ) ;
341
322
} ;
342
- break ;
343
323
}
344
324
}
345
325
346
- if ( ! matcher ) {
347
- throw new Error ( `Unknown selector type: ${ selector . type } ` ) ;
348
- }
349
- MATCHER_CACHE . set ( selector , matcher ) ;
350
- return matcher ;
326
+ throw new Error ( `Unknown selector type: ${ selector . type } ` ) ;
351
327
}
352
328
353
329
/**
@@ -377,8 +353,7 @@ function matches(node, selector, ancestry, options) {
377
353
if ( ! node ) { return false ; }
378
354
if ( ! ancestry ) { ancestry = [ ] ; }
379
355
380
- const matcher = getMatcher ( selector ) ;
381
- return matcher ( node , ancestry , options ) ;
356
+ return getMatcher ( selector ) ( node , ancestry , options ) ;
382
357
}
383
358
384
359
/**
0 commit comments