Skip to content

Commit 806c085

Browse files
separate the caching wrapper from the matcher generator
1 parent d054bcb commit 806c085

File tree

1 file changed

+55
-80
lines changed

1 file changed

+55
-80
lines changed

esquery.js

Lines changed: 55 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -78,80 +78,89 @@ function inPath(node, ancestor, path, fromPathIndex) {
7878
* A WeakMap for holding cached matcher functions for selectors.
7979
* @type {WeakMap<SelectorAST, SelectorMatcher>}
8080
*/
81-
const MATCHER_CACHE = new WeakMap();
81+
const MATCHER_CACHE = typeof WeakMap === 'function' ? new WeakMap : null;
8282

8383
/**
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.
8687
* @param {?SelectorAST} selector
8788
* @returns {SelectorMatcher}
8889
*/
8990
function getMatcher(selector) {
90-
if (!selector) {
91+
if (selector == null) {
9192
return () => true;
9293
}
9394

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);
96102
return matcher;
97103
}
98104

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) {
99114
switch(selector.type) {
100115
case 'wildcard':
101-
matcher = () => true;
102-
break;
116+
return () => true;
103117

104118
case 'identifier': {
105119
const value = selector.value.toLowerCase();
106-
matcher = (node) => value === node.type.toLowerCase();
107-
break;
120+
return (node) => value === node.type.toLowerCase();
108121
}
109122

110123
case 'field': {
111124
const path = selector.name.split('.');
112-
matcher = (node, ancestry) => {
125+
return (node, ancestry) => {
113126
const ancestor = ancestry[path.length - 1];
114127
return inPath(node, ancestor, path, 0);
115128
};
116-
break;
117129
}
118130

119131
case 'matches': {
120132
const matchers = selector.selectors.map(getMatcher);
121-
matcher = (node, ancestry, options) => {
133+
return (node, ancestry, options) => {
122134
for (let i = 0; i < matchers.length; ++i) {
123135
if (matchers[i](node, ancestry, options)) { return true; }
124136
}
125137
return false;
126138
};
127-
break;
128139
}
129140

130141
case 'compound': {
131142
const matchers = selector.selectors.map(getMatcher);
132-
matcher = (node, ancestry, options) => {
143+
return (node, ancestry, options) => {
133144
for (let i = 0; i < matchers.length; ++i) {
134145
if (!matchers[i](node, ancestry, options)) { return false; }
135146
}
136147
return true;
137148
};
138-
break;
139149
}
140150

141151
case 'not': {
142152
const matchers = selector.selectors.map(getMatcher);
143-
matcher = (node, ancestry, options) => {
153+
return (node, ancestry, options) => {
144154
for (let i = 0; i < matchers.length; ++i) {
145155
if (matchers[i](node, ancestry, options)) { return false; }
146156
}
147157
return true;
148158
};
149-
break;
150159
}
151160

152161
case 'has': {
153162
const matchers = selector.selectors.map(getMatcher);
154-
matcher = (node, ancestry, options) => {
163+
return (node, ancestry, options) => {
155164
let result = false;
156165

157166
const a = [];
@@ -174,25 +183,23 @@ function getMatcher(selector) {
174183

175184
return result;
176185
};
177-
break;
178186
}
179187

180188
case 'child': {
181189
const left = getMatcher(selector.left);
182190
const right = getMatcher(selector.right);
183-
matcher = (node, ancestry, options) => {
191+
return (node, ancestry, options) => {
184192
if (right(node, ancestry, options)) {
185193
return left(ancestry[0], ancestry.slice(1), options);
186194
}
187195
return false;
188196
};
189-
break;
190197
}
191198

192199
case 'descendant': {
193200
const left = getMatcher(selector.left);
194201
const right = getMatcher(selector.right);
195-
matcher = (node, ancestry, options) => {
202+
return (node, ancestry, options) => {
196203
if (right(node, ancestry, options)) {
197204
for (let i = 0, l = ancestry.length; i < l; ++i) {
198205
if (left(ancestry[i], ancestry.slice(i + 1), options)) {
@@ -202,119 +209,93 @@ function getMatcher(selector) {
202209
}
203210
return false;
204211
};
205-
break;
206212
}
207213

208214
case 'attribute': {
209215
const path = selector.name.split('.');
210216
switch (selector.operator) {
211217
case void 0:
212-
matcher = (node) => getPath(node, path) != null;
213-
break;
218+
return (node) => getPath(node, path) != null;
214219
case '=':
215220
switch (selector.value.type) {
216221
case 'regexp':
217-
matcher = (node) => {
222+
return (node) => {
218223
const p = getPath(node, path);
219224
return typeof p === 'string' && selector.value.value.test(p);
220225
};
221-
break;
222226
case 'literal': {
223227
const literal = `${selector.value.value}`;
224-
matcher = (node) => literal === `${getPath(node, path)}`;
225-
break;
228+
return (node) => literal === `${getPath(node, path)}`;
226229
}
227230
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);
232232
}
233-
break;
233+
throw new Error(`Unknown selector value type: ${selector.value.type}`);
234234
case '!=':
235235
switch (selector.value.type) {
236236
case 'regexp':
237-
matcher = (node) => !selector.value.value.test(getPath(node, path));
238-
break;
237+
return (node) => !selector.value.value.test(getPath(node, path));
239238
case 'literal': {
240239
const literal = `${selector.value.value}`;
241-
matcher = (node) => literal !== `${getPath(node, path)}`;
242-
break;
240+
return (node) => literal !== `${getPath(node, path)}`;
243241
}
244242
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);
249244
}
250-
break;
245+
throw new Error(`Unknown selector value type: ${selector.value.type}`);
251246
case '<=':
252-
matcher = (node) => getPath(node, path) <= selector.value.value;
253-
break;
247+
return (node) => getPath(node, path) <= selector.value.value;
254248
case '<':
255-
matcher = (node) => getPath(node, path) < selector.value.value;
256-
break;
249+
return (node) => getPath(node, path) < selector.value.value;
257250
case '>':
258-
matcher = (node) => getPath(node, path) > selector.value.value;
259-
break;
251+
return (node) => getPath(node, path) > selector.value.value;
260252
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;
265254
}
266-
break;
255+
throw new Error(`Unknown operator: ${selector.operator}`);
267256
}
268257

269258
case 'sibling': {
270259
const left = getMatcher(selector.left);
271260
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) &&
274263
sibling(node, left, ancestry, LEFT_SIDE, options) ||
275264
selector.left.subject &&
276265
left(node, ancestry, options) &&
277266
sibling(node, right, ancestry, RIGHT_SIDE, options);
278-
};
279-
break;
280267
}
281268

282269
case 'adjacent': {
283270
const left = getMatcher(selector.left);
284271
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) &&
287274
adjacent(node, left, ancestry, LEFT_SIDE, options) ||
288275
selector.right.subject &&
289276
left(node, ancestry, options) &&
290277
adjacent(node, right, ancestry, RIGHT_SIDE, options);
291-
};
292-
break;
293278
}
294279

295280
case 'nth-child': {
296281
const nth = selector.index.value;
297282
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) &&
300285
nthChild(node, ancestry, nth, options);
301-
};
302-
break;
303286
}
304287

305288
case 'nth-last-child': {
306289
const nth = -selector.index.value;
307290
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) &&
310293
nthChild(node, ancestry, nth, options);
311-
};
312-
break;
313294
}
314295

315296
case 'class': {
316297
const name = selector.name.toLowerCase();
317-
matcher = (node, ancestry) => {
298+
return (node, ancestry) => {
318299
switch(name){
319300
case 'statement':
320301
if(node.type.slice(-9) === 'Statement') return true;
@@ -339,15 +320,10 @@ function getMatcher(selector) {
339320
}
340321
throw new Error(`Unknown class name: ${selector.name}`);
341322
};
342-
break;
343323
}
344324
}
345325

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}`);
351327
}
352328

353329
/**
@@ -377,8 +353,7 @@ function matches(node, selector, ancestry, options) {
377353
if (!node) { return false; }
378354
if (!ancestry) { ancestry = []; }
379355

380-
const matcher = getMatcher(selector);
381-
return matcher(node, ancestry, options);
356+
return getMatcher(selector)(node, ancestry, options);
382357
}
383358

384359
/**

0 commit comments

Comments
 (0)