1 | /**
|
---|
2 | * @fileoverview Traverser to traverse AST trees.
|
---|
3 | * @author Nicholas C. Zakas
|
---|
4 | * @author Toru Nagashima
|
---|
5 | */
|
---|
6 | "use strict";
|
---|
7 |
|
---|
8 | //------------------------------------------------------------------------------
|
---|
9 | // Requirements
|
---|
10 | //------------------------------------------------------------------------------
|
---|
11 |
|
---|
12 | const vk = require("eslint-visitor-keys");
|
---|
13 | const debug = require("debug")("eslint:traverser");
|
---|
14 |
|
---|
15 | //------------------------------------------------------------------------------
|
---|
16 | // Helpers
|
---|
17 | //------------------------------------------------------------------------------
|
---|
18 |
|
---|
19 | /**
|
---|
20 | * Do nothing.
|
---|
21 | * @returns {void}
|
---|
22 | */
|
---|
23 | function noop() {
|
---|
24 |
|
---|
25 | // do nothing.
|
---|
26 | }
|
---|
27 |
|
---|
28 | /**
|
---|
29 | * Check whether the given value is an ASTNode or not.
|
---|
30 | * @param {any} x The value to check.
|
---|
31 | * @returns {boolean} `true` if the value is an ASTNode.
|
---|
32 | */
|
---|
33 | function isNode(x) {
|
---|
34 | return x !== null && typeof x === "object" && typeof x.type === "string";
|
---|
35 | }
|
---|
36 |
|
---|
37 | /**
|
---|
38 | * Get the visitor keys of a given node.
|
---|
39 | * @param {Object} visitorKeys The map of visitor keys.
|
---|
40 | * @param {ASTNode} node The node to get their visitor keys.
|
---|
41 | * @returns {string[]} The visitor keys of the node.
|
---|
42 | */
|
---|
43 | function getVisitorKeys(visitorKeys, node) {
|
---|
44 | let keys = visitorKeys[node.type];
|
---|
45 |
|
---|
46 | if (!keys) {
|
---|
47 | keys = vk.getKeys(node);
|
---|
48 | debug("Unknown node type \"%s\": Estimated visitor keys %j", node.type, keys);
|
---|
49 | }
|
---|
50 |
|
---|
51 | return keys;
|
---|
52 | }
|
---|
53 |
|
---|
54 | /**
|
---|
55 | * The traverser class to traverse AST trees.
|
---|
56 | */
|
---|
57 | class Traverser {
|
---|
58 | constructor() {
|
---|
59 | this._current = null;
|
---|
60 | this._parents = [];
|
---|
61 | this._skipped = false;
|
---|
62 | this._broken = false;
|
---|
63 | this._visitorKeys = null;
|
---|
64 | this._enter = null;
|
---|
65 | this._leave = null;
|
---|
66 | }
|
---|
67 |
|
---|
68 | /**
|
---|
69 | * Gives current node.
|
---|
70 | * @returns {ASTNode} The current node.
|
---|
71 | */
|
---|
72 | current() {
|
---|
73 | return this._current;
|
---|
74 | }
|
---|
75 |
|
---|
76 | /**
|
---|
77 | * Gives a copy of the ancestor nodes.
|
---|
78 | * @returns {ASTNode[]} The ancestor nodes.
|
---|
79 | */
|
---|
80 | parents() {
|
---|
81 | return this._parents.slice(0);
|
---|
82 | }
|
---|
83 |
|
---|
84 | /**
|
---|
85 | * Break the current traversal.
|
---|
86 | * @returns {void}
|
---|
87 | */
|
---|
88 | break() {
|
---|
89 | this._broken = true;
|
---|
90 | }
|
---|
91 |
|
---|
92 | /**
|
---|
93 | * Skip child nodes for the current traversal.
|
---|
94 | * @returns {void}
|
---|
95 | */
|
---|
96 | skip() {
|
---|
97 | this._skipped = true;
|
---|
98 | }
|
---|
99 |
|
---|
100 | /**
|
---|
101 | * Traverse the given AST tree.
|
---|
102 | * @param {ASTNode} node The root node to traverse.
|
---|
103 | * @param {Object} options The option object.
|
---|
104 | * @param {Object} [options.visitorKeys=DEFAULT_VISITOR_KEYS] The keys of each node types to traverse child nodes. Default is `./default-visitor-keys.json`.
|
---|
105 | * @param {Function} [options.enter=noop] The callback function which is called on entering each node.
|
---|
106 | * @param {Function} [options.leave=noop] The callback function which is called on leaving each node.
|
---|
107 | * @returns {void}
|
---|
108 | */
|
---|
109 | traverse(node, options) {
|
---|
110 | this._current = null;
|
---|
111 | this._parents = [];
|
---|
112 | this._skipped = false;
|
---|
113 | this._broken = false;
|
---|
114 | this._visitorKeys = options.visitorKeys || vk.KEYS;
|
---|
115 | this._enter = options.enter || noop;
|
---|
116 | this._leave = options.leave || noop;
|
---|
117 | this._traverse(node, null);
|
---|
118 | }
|
---|
119 |
|
---|
120 | /**
|
---|
121 | * Traverse the given AST tree recursively.
|
---|
122 | * @param {ASTNode} node The current node.
|
---|
123 | * @param {ASTNode|null} parent The parent node.
|
---|
124 | * @returns {void}
|
---|
125 | * @private
|
---|
126 | */
|
---|
127 | _traverse(node, parent) {
|
---|
128 | if (!isNode(node)) {
|
---|
129 | return;
|
---|
130 | }
|
---|
131 |
|
---|
132 | this._current = node;
|
---|
133 | this._skipped = false;
|
---|
134 | this._enter(node, parent);
|
---|
135 |
|
---|
136 | if (!this._skipped && !this._broken) {
|
---|
137 | const keys = getVisitorKeys(this._visitorKeys, node);
|
---|
138 |
|
---|
139 | if (keys.length >= 1) {
|
---|
140 | this._parents.push(node);
|
---|
141 | for (let i = 0; i < keys.length && !this._broken; ++i) {
|
---|
142 | const child = node[keys[i]];
|
---|
143 |
|
---|
144 | if (Array.isArray(child)) {
|
---|
145 | for (let j = 0; j < child.length && !this._broken; ++j) {
|
---|
146 | this._traverse(child[j], node);
|
---|
147 | }
|
---|
148 | } else {
|
---|
149 | this._traverse(child, node);
|
---|
150 | }
|
---|
151 | }
|
---|
152 | this._parents.pop();
|
---|
153 | }
|
---|
154 | }
|
---|
155 |
|
---|
156 | if (!this._broken) {
|
---|
157 | this._leave(node, parent);
|
---|
158 | }
|
---|
159 |
|
---|
160 | this._current = parent;
|
---|
161 | }
|
---|
162 |
|
---|
163 | /**
|
---|
164 | * Calculates the keys to use for traversal.
|
---|
165 | * @param {ASTNode} node The node to read keys from.
|
---|
166 | * @returns {string[]} An array of keys to visit on the node.
|
---|
167 | * @private
|
---|
168 | */
|
---|
169 | static getKeys(node) {
|
---|
170 | return vk.getKeys(node);
|
---|
171 | }
|
---|
172 |
|
---|
173 | /**
|
---|
174 | * Traverse the given AST tree.
|
---|
175 | * @param {ASTNode} node The root node to traverse.
|
---|
176 | * @param {Object} options The option object.
|
---|
177 | * @param {Object} [options.visitorKeys=DEFAULT_VISITOR_KEYS] The keys of each node types to traverse child nodes. Default is `./default-visitor-keys.json`.
|
---|
178 | * @param {Function} [options.enter=noop] The callback function which is called on entering each node.
|
---|
179 | * @param {Function} [options.leave=noop] The callback function which is called on leaving each node.
|
---|
180 | * @returns {void}
|
---|
181 | */
|
---|
182 | static traverse(node, options) {
|
---|
183 | new Traverser().traverse(node, options);
|
---|
184 | }
|
---|
185 |
|
---|
186 | /**
|
---|
187 | * The default visitor keys.
|
---|
188 | * @type {Object}
|
---|
189 | */
|
---|
190 | static get DEFAULT_VISITOR_KEYS() {
|
---|
191 | return vk.KEYS;
|
---|
192 | }
|
---|
193 | }
|
---|
194 |
|
---|
195 | module.exports = Traverser;
|
---|