• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import { visitorKeys } from '@typescript-eslint/visitor-keys';
2import { TSESTree } from './ts-estree';
3
4// eslint-disable-next-line @typescript-eslint/no-explicit-any
5function isValidNode(x: any): x is TSESTree.Node {
6  return x !== null && typeof x === 'object' && typeof x.type === 'string';
7}
8
9function getVisitorKeysForNode(
10  allVisitorKeys: typeof visitorKeys,
11  node: TSESTree.Node,
12): readonly (keyof TSESTree.Node)[] {
13  const keys = allVisitorKeys[node.type];
14  return (keys ?? []) as never;
15}
16
17type SimpleTraverseOptions =
18  | {
19      enter: (node: TSESTree.Node, parent: TSESTree.Node | undefined) => void;
20    }
21  | {
22      [key: string]: (
23        node: TSESTree.Node,
24        parent: TSESTree.Node | undefined,
25      ) => void;
26    };
27
28class SimpleTraverser {
29  private readonly allVisitorKeys = visitorKeys;
30  private readonly selectors: SimpleTraverseOptions;
31  private readonly setParentPointers: boolean;
32
33  constructor(selectors: SimpleTraverseOptions, setParentPointers = false) {
34    this.selectors = selectors;
35    this.setParentPointers = setParentPointers;
36  }
37
38  traverse(node: unknown, parent: TSESTree.Node | undefined): void {
39    if (!isValidNode(node)) {
40      return;
41    }
42
43    if (this.setParentPointers) {
44      node.parent = parent;
45    }
46
47    if ('enter' in this.selectors) {
48      this.selectors.enter(node, parent);
49    } else if (node.type in this.selectors) {
50      this.selectors[node.type](node, parent);
51    }
52
53    const keys = getVisitorKeysForNode(this.allVisitorKeys, node);
54    if (keys.length < 1) {
55      return;
56    }
57
58    for (const key of keys) {
59      const childOrChildren = node[key];
60
61      if (Array.isArray(childOrChildren)) {
62        for (const child of childOrChildren) {
63          this.traverse(child, node);
64        }
65      } else {
66        this.traverse(childOrChildren, node);
67      }
68    }
69  }
70}
71
72export function simpleTraverse(
73  startingNode: TSESTree.Node,
74  options: SimpleTraverseOptions,
75  setParentPointers = false,
76): void {
77  new SimpleTraverser(options, setParentPointers).traverse(
78    startingNode,
79    undefined,
80  );
81}
82