astronomical 2.0.1 → 2.1.1-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cjs/index.js CHANGED
@@ -34,9 +34,16 @@ exports.functions = {
34
34
  },
35
35
  "concat": {
36
36
  fn: (result) => {
37
- if (result.some(x => x.length == 0))
38
- return [];
39
- return [result.flat().join("")];
37
+ // Optimize: combine empty check with manual flattening
38
+ const flattened = [];
39
+ for (let i = 0; i < result.length; i++) {
40
+ if (result[i].length === 0)
41
+ return [];
42
+ for (let j = 0; j < result[i].length; j++) {
43
+ flattened.push(result[i][j]);
44
+ }
45
+ }
46
+ return [flattened.join("")];
40
47
  }
41
48
  },
42
49
  "first": {
@@ -45,7 +52,7 @@ exports.functions = {
45
52
  throw new Error("Invalid number of arugments for first");
46
53
  if (result[0].length == 0)
47
54
  return [];
48
- return [result.map(r => r[0])[0]];
55
+ return [result[0][0]];
49
56
  }
50
57
  },
51
58
  "nthchild": {
@@ -79,14 +86,14 @@ function createQuerier() {
79
86
  const traverser = createTraverser();
80
87
  const { getChildren, getPrimitiveChildren, getPrimitiveChildrenOrNodePaths, getBinding, createNodePath, traverse } = traverser;
81
88
  function createFilter(filter, filterResult) {
82
- if (filter.type == "and" || filter.type == "or" || filter.type == "equals") {
89
+ if (filter.type == parseQuery_1.NodeType.AND || filter.type == parseQuery_1.NodeType.OR || filter.type == parseQuery_1.NodeType.EQUALS) {
83
90
  return {
84
91
  type: filter.type,
85
92
  left: createFilter(filter.left, []),
86
93
  right: createFilter(filter.right, [])
87
94
  };
88
95
  }
89
- else if (filter.type == "literal") {
96
+ else if (filter.type == parseQuery_1.NodeType.LITERAL) {
90
97
  const r = [filter.value];
91
98
  return {
92
99
  node: filter,
@@ -102,16 +109,16 @@ function createQuerier() {
102
109
  };
103
110
  }
104
111
  function addFilterChildrenToState(filter, state) {
105
- if ("type" in filter && (filter.type == "and" || filter.type == "or" || filter.type == "equals")) {
112
+ if ("type" in filter && (filter.type == parseQuery_1.NodeType.AND || filter.type == parseQuery_1.NodeType.OR || filter.type == parseQuery_1.NodeType.EQUALS)) {
106
113
  addFilterChildrenToState(filter.left, state);
107
114
  addFilterChildrenToState(filter.right, state);
108
115
  }
109
116
  else if ("node" in filter) {
110
- if (filter.node.type == "child") {
117
+ if (filter.node.type == parseQuery_1.NodeType.CHILD) {
111
118
  log?.debug("ADDING FILTER CHILD", filter.node);
112
119
  state.child[state.depth + 1].push(filter);
113
120
  }
114
- if (filter.node.type == "descendant") {
121
+ if (filter.node.type == parseQuery_1.NodeType.DESCENDANT) {
115
122
  log?.debug("ADDING FILTER DESCENDANT", filter.node);
116
123
  state.descendant[state.depth + 1].push(filter);
117
124
  }
@@ -120,10 +127,10 @@ function createQuerier() {
120
127
  function createFNodeAndAddToState(token, result, state) {
121
128
  log?.debug("ADDING FNODE", token);
122
129
  const fnode = createFNode(token, result);
123
- if (token.type == "child") {
130
+ if (token.type == parseQuery_1.NodeType.CHILD) {
124
131
  state.child[state.depth + 1].push(fnode);
125
132
  }
126
- else if (token.type == "descendant") {
133
+ else if (token.type == parseQuery_1.NodeType.DESCENDANT) {
127
134
  state.descendant[state.depth + 1].push(fnode);
128
135
  }
129
136
  return fnode;
@@ -161,7 +168,7 @@ function createQuerier() {
161
168
  addFilterChildrenToState(filter, state);
162
169
  const child = fnode.node.child;
163
170
  if (child) {
164
- if (child.type == "function") {
171
+ if (child.type == parseQuery_1.NodeType.FUNCTION) {
165
172
  const fr = addFunction(fnode, child, path, state);
166
173
  state.functionCalls[state.depth].push(fr);
167
174
  }
@@ -172,7 +179,7 @@ function createQuerier() {
172
179
  }
173
180
  else {
174
181
  const child = fnode.node.child;
175
- if (child?.type == "function") {
182
+ if (child?.type == parseQuery_1.NodeType.FUNCTION) {
176
183
  const fr = addFunction(fnode, child, path, state);
177
184
  state.functionCalls[state.depth].push(fr);
178
185
  }
@@ -184,11 +191,11 @@ function createQuerier() {
184
191
  function addFunction(rootNode, functionCall, path, state) {
185
192
  const functionNode = { node: rootNode.node, functionCall: functionCall, parameters: [], result: [] };
186
193
  for (const param of functionCall.parameters) {
187
- if (param.type == "literal") {
194
+ if (param.type == parseQuery_1.NodeType.LITERAL) {
188
195
  functionNode.parameters.push({ node: param, result: [param.value] });
189
196
  }
190
197
  else {
191
- if (param.type == "function") {
198
+ if (param.type == parseQuery_1.NodeType.FUNCTION) {
192
199
  functionNode.parameters.push(addFunction(functionNode, param, path, state));
193
200
  }
194
201
  else {
@@ -214,7 +221,7 @@ function createQuerier() {
214
221
  function evaluateFilter(filter, path) {
215
222
  log?.debug("EVALUATING FILTER", filter, breadCrumb(path));
216
223
  if ("type" in filter) {
217
- if (filter.type == "and") {
224
+ if (filter.type == parseQuery_1.NodeType.AND) {
218
225
  const left = evaluateFilter(filter.left, path);
219
226
  if (left.length == 0) {
220
227
  return [];
@@ -222,7 +229,7 @@ function createQuerier() {
222
229
  const r = evaluateFilter(filter.right, path);
223
230
  return r;
224
231
  }
225
- if (filter.type == "or") {
232
+ if (filter.type == parseQuery_1.NodeType.OR) {
226
233
  const left = evaluateFilter(filter.left, path);
227
234
  if (left.length > 0) {
228
235
  return left;
@@ -230,15 +237,30 @@ function createQuerier() {
230
237
  const r = evaluateFilter(filter.right, path);
231
238
  return r;
232
239
  }
233
- if (filter.type == "equals") {
240
+ if (filter.type == parseQuery_1.NodeType.EQUALS) {
234
241
  const left = evaluateFilter(filter.left, path);
235
242
  const right = evaluateFilter(filter.right, path);
236
- const r = left.filter(x => right.includes(x));
243
+ // Optimize: use Set for O(1) lookups instead of O(n) includes
244
+ if (right.length > 3) {
245
+ const rightSet = new Set(right);
246
+ const r = [];
247
+ for (let i = 0; i < left.length; i++) {
248
+ if (rightSet.has(left[i]))
249
+ r.push(left[i]);
250
+ }
251
+ return r;
252
+ }
253
+ // For small arrays, includes is faster than Set creation
254
+ const r = [];
255
+ for (let i = 0; i < left.length; i++) {
256
+ if (right.includes(left[i]))
257
+ r.push(left[i]);
258
+ }
237
259
  return r;
238
260
  }
239
261
  throw new Error("Unknown filter type: " + filter.type);
240
262
  }
241
- if (filter.node.type == "parent") {
263
+ if (filter.node.type == parseQuery_1.NodeType.PARENT) {
242
264
  const r = resolveFilterWithParent(filter.node, path);
243
265
  return r;
244
266
  }
@@ -261,7 +283,7 @@ function createQuerier() {
261
283
  function resolveFilterWithParent(node, path) {
262
284
  let startNode = node;
263
285
  let startPath = path;
264
- while (startNode.type == "parent") {
286
+ while (startNode.type == parseQuery_1.NodeType.PARENT) {
265
287
  if (!startNode.child)
266
288
  throw new Error("Parent filter must have child");
267
289
  if (!startPath.parentPath)
@@ -272,40 +294,80 @@ function createQuerier() {
272
294
  }
273
295
  return resolveDirectly(startNode, startPath);
274
296
  }
275
- function isDefined(value) {
276
- return value != undefined && value != null;
277
- }
278
297
  let subQueryCounter = 0;
279
298
  const memo = new Map();
280
299
  function resolveDirectly(node, path) {
281
300
  let startNode = node;
282
301
  const startPath = path;
283
302
  let paths = [startPath];
284
- while (startNode.attribute && startNode.type == "child") {
303
+ while (startNode.attribute && startNode.type == parseQuery_1.NodeType.CHILD) {
285
304
  const lookup = startNode.value;
286
305
  if (!lookup)
287
306
  throw new Error("Selector must have a value");
288
307
  //log?.debug("STEP IN ", lookup, paths.map(p => breadCrumb(p)));
289
- const nodes = paths.filter(nodeutils_1.isNodePath).map(n => getPrimitiveChildrenOrNodePaths(lookup, n)).flat();
290
- //log?.debug("LOOKUP", lookup, path.node.type, nodes.map(n => n.node));
291
- //console.log(nodes);
308
+ // Optimize: avoid filter().map().flat() chain - use single loop
309
+ const nodes = [];
310
+ for (let i = 0; i < paths.length; i++) {
311
+ const p = paths[i];
312
+ if (!(0, nodeutils_1.isNodePath)(p))
313
+ continue;
314
+ const arr = getPrimitiveChildrenOrNodePaths(lookup, p);
315
+ for (let j = 0; j < arr.length; j++) {
316
+ nodes.push(arr[j]);
317
+ }
318
+ }
292
319
  if (nodes.length == 0)
293
320
  return [];
294
321
  paths = nodes;
295
322
  if (startNode.resolve) {
296
- const resolved = paths.filter(nodeutils_1.isNodePath).map(p => resolveBinding(p)).filter(isDefined).map(p => getChildren("init", p)).flat();
323
+ const resolved = [];
324
+ for (let i = 0; i < paths.length; i++) {
325
+ const p = paths[i];
326
+ if (!(0, nodeutils_1.isNodePath)(p))
327
+ continue;
328
+ const binding = resolveBinding(p);
329
+ if (!binding)
330
+ continue;
331
+ const children = getChildren("init", binding);
332
+ for (let j = 0; j < children.length; j++) {
333
+ resolved.push(children[j]);
334
+ }
335
+ }
297
336
  if (resolved.length > 0)
298
337
  paths = resolved;
299
338
  }
300
339
  else if (startNode.binding) {
301
- paths = paths.filter(nodeutils_1.isNodePath).map(p => resolveBinding(p)).filter(isDefined);
340
+ const bindings = [];
341
+ for (let i = 0; i < paths.length; i++) {
342
+ const p = paths[i];
343
+ if (!(0, nodeutils_1.isNodePath)(p))
344
+ continue;
345
+ const binding = resolveBinding(p);
346
+ if (binding)
347
+ bindings.push(binding);
348
+ }
349
+ paths = bindings;
302
350
  }
303
351
  const filter = startNode.filter;
304
352
  if (filter) {
305
- paths = paths.filter(nodeutils_1.isNodePath).filter(p => travHandle({ subquery: filter }, p).subquery.length > 0);
353
+ const filtered = [];
354
+ for (let i = 0; i < paths.length; i++) {
355
+ const p = paths[i];
356
+ if (!(0, nodeutils_1.isNodePath)(p))
357
+ continue;
358
+ if (travHandle({ subquery: filter }, p).subquery.length > 0) {
359
+ filtered.push(p);
360
+ }
361
+ }
362
+ paths = filtered;
306
363
  }
307
364
  if (!startNode.child) {
308
- return paths.map(p => (0, nodeutils_1.isPrimitive)(p) ? p : p.node);
365
+ const results = new Array(paths.length);
366
+ for (let i = 0; i < paths.length; i++) {
367
+ const p = paths[i];
368
+ results[i] = (0, nodeutils_1.isPrimitive)(p) ? p : p.node;
369
+ }
370
+ return results;
309
371
  }
310
372
  startNode = startNode.child;
311
373
  }
@@ -315,7 +377,10 @@ function createQuerier() {
315
377
  for (const path of paths) {
316
378
  if ((0, nodeutils_1.isNodePath)(path)) {
317
379
  if (memo.has(startNode) && memo.get(startNode).has(path)) {
318
- result.push(...memo.get(startNode).get(path));
380
+ const cached = memo.get(startNode).get(path);
381
+ for (let i = 0; i < cached.length; i++) {
382
+ result.push(cached[i]);
383
+ }
319
384
  }
320
385
  else {
321
386
  const subQueryKey = "subquery-" + subQueryCounter++;
@@ -323,7 +388,9 @@ function createQuerier() {
323
388
  if (!memo.has(startNode))
324
389
  memo.set(startNode, new Map());
325
390
  memo.get(startNode)?.set(path, subQueryResult);
326
- result.push(...subQueryResult);
391
+ for (let i = 0; i < subQueryResult.length; i++) {
392
+ result.push(subQueryResult[i]);
393
+ }
327
394
  }
328
395
  }
329
396
  }
@@ -336,14 +403,16 @@ function createQuerier() {
336
403
  const filters = [];
337
404
  const nodeFilters = state.filtersMap[state.depth].get(fnode.node);
338
405
  if (nodeFilters) {
339
- for (const f of nodeFilters) {
406
+ for (let i = 0; i < nodeFilters.length; i++) {
407
+ const f = nodeFilters[i];
340
408
  if (f.qNode !== fnode.node)
341
409
  continue;
342
410
  if (f.node !== path.node)
343
411
  continue;
344
412
  filters.push(f);
345
413
  }
346
- for (const f of filters) {
414
+ for (let i = 0; i < filters.length; i++) {
415
+ const f = filters[i];
347
416
  if (evaluateFilter(f.filter, path).length > 0) {
348
417
  matchingFilters.push(f);
349
418
  }
@@ -356,7 +425,9 @@ function createQuerier() {
356
425
  const resolved = binding ? getChildren("init", binding)[0] : undefined;
357
426
  if (fnode.node.child) {
358
427
  const result = resolveDirectly(fnode.node.child, resolved ?? path);
359
- fnode.result.push(...result);
428
+ for (let i = 0; i < result.length; i++) {
429
+ fnode.result.push(result[i]);
430
+ }
360
431
  }
361
432
  else {
362
433
  fnode.result.push(path.node);
@@ -367,7 +438,9 @@ function createQuerier() {
367
438
  if (binding) {
368
439
  if (fnode.node.child) {
369
440
  const result = resolveDirectly(fnode.node.child, binding);
370
- fnode.result.push(...result);
441
+ for (let i = 0; i < result.length; i++) {
442
+ fnode.result.push(result[i]);
443
+ }
371
444
  }
372
445
  else {
373
446
  fnode.result.push(binding.node);
@@ -377,7 +450,7 @@ function createQuerier() {
377
450
  else if (!fnode.node.child) {
378
451
  fnode.result.push(path.node);
379
452
  }
380
- else if (fnode.node.child.type == "function") {
453
+ else if (fnode.node.child.type == parseQuery_1.NodeType.FUNCTION) {
381
454
  const functionCallResult = state.functionCalls[state.depth].find(f => f.node == fnode.node);
382
455
  if (!functionCallResult)
383
456
  throw new Error("Did not find expected function call for " + fnode.node.child.function);
@@ -385,12 +458,18 @@ function createQuerier() {
385
458
  }
386
459
  else if (matchingFilters.length > 0) {
387
460
  log?.debug("HAS MATCHING FILTER", fnode.result.length, matchingFilters.length, breadCrumb(path));
388
- fnode.result.push(...matchingFilters.flatMap(f => f.result));
461
+ for (let i = 0; i < matchingFilters.length; i++) {
462
+ const filterResult = matchingFilters[i].result;
463
+ for (let j = 0; j < filterResult.length; j++) {
464
+ fnode.result.push(filterResult[j]);
465
+ }
466
+ }
389
467
  }
390
468
  }
391
469
  function resolveFunctionCalls(fnode, functionCallResult, path, state) {
392
470
  const parameterResults = [];
393
- for (const p of functionCallResult.parameters) {
471
+ for (let i = 0; i < functionCallResult.parameters.length; i++) {
472
+ const p = functionCallResult.parameters[i];
394
473
  if ("parameters" in p) {
395
474
  resolveFunctionCalls(p, p, path, state);
396
475
  parameterResults.push(p.result);
@@ -401,10 +480,17 @@ function createQuerier() {
401
480
  }
402
481
  const functionResult = exports.functions[functionCallResult.functionCall.function].fn(parameterResults);
403
482
  log?.debug("PARAMETER RESULTS", functionCallResult.functionCall.function, parameterResults, functionResult);
404
- fnode.result.push(...functionResult);
483
+ for (let i = 0; i < functionResult.length; i++) {
484
+ fnode.result.push(functionResult[i]);
485
+ }
405
486
  }
406
487
  function travHandle(queries, root) {
407
- const results = Object.fromEntries(Object.keys(queries).map(name => [name, []]));
488
+ // Optimize: create results object directly instead of Object.fromEntries + map
489
+ const results = {};
490
+ const queryKeys = Object.keys(queries);
491
+ for (let i = 0; i < queryKeys.length; i++) {
492
+ results[queryKeys[i]] = [];
493
+ }
408
494
  const state = {
409
495
  depth: 0,
410
496
  child: [[], []],
@@ -417,8 +503,18 @@ function createQuerier() {
417
503
  for (const [name, node] of Object.entries(queries)) {
418
504
  createFNodeAndAddToState(node, results[name], state);
419
505
  }
420
- state.child[state.depth + 1].forEach(fnode => addPrimitiveAttributeIfMatch(fnode, root));
421
- state.descendant.slice(0, state.depth + 1).forEach(fnodes => fnodes.forEach(fnode => addPrimitiveAttributeIfMatch(fnode, root)));
506
+ // Optimize: replace forEach with for loop
507
+ const childAtDepth = state.child[state.depth + 1];
508
+ for (let i = 0; i < childAtDepth.length; i++) {
509
+ addPrimitiveAttributeIfMatch(childAtDepth[i], root);
510
+ }
511
+ const descendantSlice = state.descendant.slice(0, state.depth + 1);
512
+ for (let i = 0; i < descendantSlice.length; i++) {
513
+ const fnodes = descendantSlice[i];
514
+ for (let j = 0; j < fnodes.length; j++) {
515
+ addPrimitiveAttributeIfMatch(fnodes[j], root);
516
+ }
517
+ }
422
518
  traverse(root.node, {
423
519
  enter(path, state) {
424
520
  //log?.debug("ENTER", breadCrumb(path));
@@ -441,14 +537,20 @@ function createQuerier() {
441
537
  exit(path, state) {
442
538
  log?.debug("EXIT", breadCrumb(path));
443
539
  // Check for attributes as not all attributes are visited
444
- state.child[state.depth + 1].forEach(fnode => addPrimitiveAttributeIfMatch(fnode, path));
445
- for (const fnodes of state.descendant) {
446
- for (const fnode of fnodes) {
447
- addPrimitiveAttributeIfMatch(fnode, path);
540
+ // Optimize: replace forEach with for loop
541
+ const childAtDepthPlusOne = state.child[state.depth + 1];
542
+ for (let i = 0; i < childAtDepthPlusOne.length; i++) {
543
+ addPrimitiveAttributeIfMatch(childAtDepthPlusOne[i], path);
544
+ }
545
+ for (let i = 0; i < state.descendant.length; i++) {
546
+ const fnodes = state.descendant[i];
547
+ for (let j = 0; j < fnodes.length; j++) {
548
+ addPrimitiveAttributeIfMatch(fnodes[j], path);
448
549
  }
449
550
  }
450
- for (const [fNode, path] of state.matches[state.depth]) {
451
- addResultIfTokenMatch(fNode, path, state);
551
+ const matchesAtDepth = state.matches[state.depth];
552
+ for (let i = 0; i < matchesAtDepth.length; i++) {
553
+ addResultIfTokenMatch(matchesAtDepth[i][0], matchesAtDepth[i][1], state);
452
554
  }
453
555
  state.depth--;
454
556
  state.child.pop();
@@ -486,7 +588,13 @@ function multiQuery(code, namedQueries, returnAST) {
486
588
  const ast = typeof code == "string" ? parseSource(code) : code;
487
589
  if (ast == null)
488
590
  throw new Error("Could not pase code");
489
- const queries = Object.fromEntries(Object.entries(namedQueries).map(([name, query]) => [name, (0, parseQuery_1.parse)(query)]));
591
+ // Optimize: parse queries directly instead of Object.fromEntries + map
592
+ const queries = {};
593
+ const entries = Object.entries(namedQueries);
594
+ for (let i = 0; i < entries.length; i++) {
595
+ const [name, queryStr] = entries[i];
596
+ queries[name] = (0, parseQuery_1.parse)(queryStr);
597
+ }
490
598
  const querier = createQuerier();
491
599
  const result = querier.beginHandle(queries, ast);
492
600
  log?.debug("Query time: ", Date.now() - start);
@@ -554,7 +662,12 @@ function createTraverser() {
554
662
  if (key in path.node) {
555
663
  const r = path.node[key];
556
664
  if (Array.isArray(r)) {
557
- return r.map((n, i) => createNodePath(n, i, key, path.scopeId, path.functionScopeId, path));
665
+ const len = r.length;
666
+ const result = new Array(len);
667
+ for (let i = 0; i < len; i++) {
668
+ result[i] = createNodePath(r[i], i, key, path.scopeId, path.functionScopeId, path);
669
+ }
670
+ return result;
558
671
  }
559
672
  else if (r != undefined) {
560
673
  return [createNodePath(r, key, key, path.scopeId, path.functionScopeId, path)];
@@ -565,7 +678,16 @@ function createTraverser() {
565
678
  function getPrimitiveChildren(key, path) {
566
679
  if (key in path.node) {
567
680
  const r = path.node[key];
568
- return (0, utils_1.toArray)(r).filter(utils_1.isDefined).filter(nodeutils_1.isPrimitive);
681
+ const arr = (0, utils_1.toArray)(r);
682
+ // Optimize: single loop instead of chained filter()
683
+ const result = [];
684
+ for (let i = 0; i < arr.length; i++) {
685
+ const item = arr[i];
686
+ if ((0, utils_1.isDefined)(item) && (0, nodeutils_1.isPrimitive)(item)) {
687
+ result.push(item);
688
+ }
689
+ }
690
+ return result;
569
691
  }
570
692
  return [];
571
693
  }
@@ -573,23 +695,29 @@ function createTraverser() {
573
695
  if (key in path.node) {
574
696
  const r = path.node[key];
575
697
  if (Array.isArray(r)) {
576
- return r.map((n, i) => (0, nodeutils_1.isPrimitive)(n) ? n :
577
- // isLiteral(n) ? n.value as PrimitiveValue :
578
- createNodePath(n, i, key, path.scopeId, path.functionScopeId, path));
698
+ const len = r.length;
699
+ const result = new Array(len);
700
+ for (let i = 0; i < len; i++) {
701
+ const n = r[i];
702
+ result[i] = (0, nodeutils_1.isPrimitive)(n) ? n : createNodePath(n, i, key, path.scopeId, path.functionScopeId, path);
703
+ }
704
+ return result;
579
705
  }
580
706
  else if (r != undefined) {
581
707
  return [
582
708
  (0, nodeutils_1.isPrimitive)(r) ? r :
583
- // isLiteral(r) ? r.value as PrimitiveValue :
584
709
  createNodePath(r, key, key, path.scopeId, path.functionScopeId, path)
585
710
  ];
586
711
  }
587
712
  }
588
713
  return [];
589
714
  }
715
+ const nodePathMap = new WeakMap();
590
716
  function createNodePath(node, key, parentKey, scopeId, functionScopeId, nodePath) {
591
- if (node.extra?.nodePath) {
592
- const path = node.extra.nodePath;
717
+ if (nodePathMap.has(node)) {
718
+ //if (node.extra?.nodePath) {
719
+ //const path = node.extra.nodePath;
720
+ const path = nodePathMap.get(node);
593
721
  if (nodePath && (0, nodeutils_1.isExportSpecifier)(nodePath.node) && key == "exported" && path.key == "local") {
594
722
  //Special handling for "export { someName }" as id is both local and exported
595
723
  path.key = "exported";
@@ -615,9 +743,10 @@ function createTraverser() {
615
743
  parentKey
616
744
  };
617
745
  if ((0, nodeutils_1.isNode)(node)) {
618
- node.extra = node.extra ?? {};
619
- node.extra.nodePath = path;
620
- Object.defineProperty(node.extra, "nodePath", { enumerable: false });
746
+ //node.extra = node.extra ?? {};
747
+ //node.extra.nodePath = path;
748
+ //Object.defineProperty(node.extra, "nodePath", { enumerable: false });
749
+ nodePathMap.set(node, path);
621
750
  }
622
751
  nodePathsCreated[node.type] = (nodePathsCreated[node.type] ?? 0) + 1;
623
752
  pathsCreated++;
@@ -671,11 +800,13 @@ function createTraverser() {
671
800
  if ((0, nodeutils_1.isScopable)(node)) {
672
801
  childScopeId = createScope(scopeId);
673
802
  }
674
- for (const key of keys) {
803
+ for (let keyIdx = 0; keyIdx < keys.length; keyIdx++) {
804
+ const key = keys[keyIdx];
675
805
  const childNodes = node[key];
676
- const children = (0, utils_1.toArray)(childNodes).filter(utils_1.isDefined);
677
- for (const [i, child] of children.entries()) {
678
- if (!(0, nodeutils_1.isNode)(child))
806
+ const children = (0, utils_1.toArray)(childNodes);
807
+ for (let i = 0; i < children.length; i++) {
808
+ const child = children[i];
809
+ if (!(0, utils_1.isDefined)(child) || !(0, nodeutils_1.isNode)(child))
679
810
  continue;
680
811
  const f = key === "body" && ((0, nodeutils_1.isFunctionDeclaration)(node) || (0, nodeutils_1.isFunctionExpression)(node)) ? childScopeId : functionScopeId;
681
812
  stack.push(child);
@@ -696,20 +827,37 @@ function createTraverser() {
696
827
  }
697
828
  function traverseInner(node, visitor, scopeId, functionScopeId, state, path) {
698
829
  const nodePath = path ?? createNodePath(node, undefined, undefined, scopeId, functionScopeId);
699
- const keys = nodeutils_1.VISITOR_KEYS[node.type] ?? [];
700
- if (nodePath.parentPath)
701
- registerBindings([nodePath.parentPath.parentPath?.node, nodePath.parentPath.node, nodePath.node].filter(utils_1.isDefined), nodePath.scopeId, nodePath.functionScopeId);
702
- for (const key of keys) {
830
+ const keys = nodeutils_1.VISITOR_KEYS[node.type];
831
+ if (nodePath.parentPath) {
832
+ const stack = [];
833
+ if (nodePath.parentPath.parentPath?.node)
834
+ stack.push(nodePath.parentPath.parentPath.node);
835
+ stack.push(nodePath.parentPath.node, nodePath.node);
836
+ registerBindings(stack, nodePath.scopeId, nodePath.functionScopeId);
837
+ }
838
+ // Optimization: Check if we need to traverse children at all
839
+ // If there are no descendant queries and no child queries at next depth, skip traversal
840
+ const stateTyped = state;
841
+ const hasDescendantQueries = stateTyped.descendant && stateTyped.descendant.some(arr => arr.length > 0);
842
+ const hasChildQueriesAtNextDepth = stateTyped.child && stateTyped.child[stateTyped.depth + 1] && stateTyped.child[stateTyped.depth + 1].length > 0;
843
+ // If no queries would match in this subtree, skip traversal entirely
844
+ if (!hasDescendantQueries && !hasChildQueriesAtNextDepth) {
845
+ return;
846
+ }
847
+ for (let keyIdx = 0; keyIdx < keys.length; keyIdx++) {
848
+ const key = keys[keyIdx];
703
849
  const childNodes = node[key];
704
850
  const children = Array.isArray(childNodes) ? childNodes : childNodes ? [childNodes] : [];
705
851
  const nodePaths = [];
706
- for (const [i, child] of children.entries()) {
852
+ for (let i = 0; i < children.length; i++) {
853
+ const child = children[i];
707
854
  if ((0, nodeutils_1.isNode)(child)) {
708
855
  const childPath = createNodePath(child, Array.isArray(childNodes) ? i : key, key, nodePath.scopeId, nodePath.functionScopeId, nodePath);
709
856
  nodePaths.push(childPath);
710
857
  }
711
858
  }
712
- for (const childPath of nodePaths) {
859
+ for (let i = 0; i < nodePaths.length; i++) {
860
+ const childPath = nodePaths[i];
713
861
  visitor.enter(childPath, state);
714
862
  traverseInner(childPath.node, visitor, nodePath.scopeId, nodePath.functionScopeId, state, childPath);
715
863
  visitor.exit(childPath, state);