• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/types';
2import { visitorKeys, VisitorKeys } from '@typescript-eslint/visitor-keys';
3
4interface VisitorOptions {
5  childVisitorKeys?: VisitorKeys | null;
6}
7
8function isObject(obj: unknown): obj is Record<string, unknown> {
9  return typeof obj === 'object' && obj != null;
10}
11function isNode(node: unknown): node is TSESTree.Node {
12  return isObject(node) && typeof node.type === 'string';
13}
14
15type NodeVisitor = {
16  [K in AST_NODE_TYPES]?: (node: TSESTree.Node) => void;
17};
18
19abstract class VisitorBase {
20  readonly #childVisitorKeys: VisitorKeys;
21  constructor(options: VisitorOptions) {
22    this.#childVisitorKeys = options.childVisitorKeys ?? visitorKeys;
23  }
24
25  /**
26   * Default method for visiting children.
27   * @param node the node whose children should be visited
28   * @param exclude a list of keys to not visit
29   */
30  visitChildren<T extends TSESTree.Node>(
31    node: T | null | undefined,
32    excludeArr?: (keyof T)[],
33  ): void {
34    if (node == null || node.type == null) {
35      return;
36    }
37
38    const exclude = new Set(excludeArr) as Set<string>;
39    const children = this.#childVisitorKeys[node.type] ?? Object.keys(node);
40    for (const key of children) {
41      if (exclude.has(key)) {
42        continue;
43      }
44
45      const child = node[key as keyof TSESTree.Node] as unknown;
46      if (!child) {
47        continue;
48      }
49
50      if (Array.isArray(child)) {
51        for (const subChild of child) {
52          if (isNode(subChild)) {
53            this.visit(subChild);
54          }
55        }
56      } else if (isNode(child)) {
57        this.visit(child);
58      }
59    }
60  }
61
62  /**
63   * Dispatching node.
64   */
65  visit(node: TSESTree.Node | null | undefined): void {
66    if (node == null || node.type == null) {
67      return;
68    }
69
70    const visitor = (this as NodeVisitor)[node.type];
71    if (visitor) {
72      return visitor.call(this, node);
73    }
74
75    this.visitChildren(node);
76  }
77}
78
79export { VisitorBase, VisitorOptions, VisitorKeys };
80