Spaces:
Sleeping
Sleeping
| /** | |
| * @fileoverview Traverser to traverse AST trees. | |
| * @author Nicholas C. Zakas | |
| * @author Toru Nagashima | |
| */ | |
| ; | |
| //------------------------------------------------------------------------------ | |
| // Requirements | |
| //------------------------------------------------------------------------------ | |
| const vk = require("eslint-visitor-keys"); | |
| const debug = require("debug")("eslint:traverser"); | |
| //------------------------------------------------------------------------------ | |
| // Helpers | |
| //------------------------------------------------------------------------------ | |
| /** | |
| * Do nothing. | |
| * @returns {void} | |
| */ | |
| function noop() { | |
| // do nothing. | |
| } | |
| /** | |
| * Check whether the given value is an ASTNode or not. | |
| * @param {any} x The value to check. | |
| * @returns {boolean} `true` if the value is an ASTNode. | |
| */ | |
| function isNode(x) { | |
| return x !== null && typeof x === "object" && typeof x.type === "string"; | |
| } | |
| /** | |
| * Get the visitor keys of a given node. | |
| * @param {Object} visitorKeys The map of visitor keys. | |
| * @param {ASTNode} node The node to get their visitor keys. | |
| * @returns {string[]} The visitor keys of the node. | |
| */ | |
| function getVisitorKeys(visitorKeys, node) { | |
| let keys = visitorKeys[node.type]; | |
| if (!keys) { | |
| keys = vk.getKeys(node); | |
| debug( | |
| 'Unknown node type "%s": Estimated visitor keys %j', | |
| node.type, | |
| keys, | |
| ); | |
| } | |
| return keys; | |
| } | |
| /** | |
| * The traverser class to traverse AST trees. | |
| */ | |
| class Traverser { | |
| constructor() { | |
| this._current = null; | |
| this._parents = []; | |
| this._skipped = false; | |
| this._broken = false; | |
| this._visitorKeys = null; | |
| this._enter = null; | |
| this._leave = null; | |
| } | |
| /** | |
| * Gives current node. | |
| * @returns {ASTNode} The current node. | |
| */ | |
| current() { | |
| return this._current; | |
| } | |
| /** | |
| * Gives a copy of the ancestor nodes. | |
| * @returns {ASTNode[]} The ancestor nodes. | |
| */ | |
| parents() { | |
| return this._parents.slice(0); | |
| } | |
| /** | |
| * Break the current traversal. | |
| * @returns {void} | |
| */ | |
| break() { | |
| this._broken = true; | |
| } | |
| /** | |
| * Skip child nodes for the current traversal. | |
| * @returns {void} | |
| */ | |
| skip() { | |
| this._skipped = true; | |
| } | |
| /** | |
| * Traverse the given AST tree. | |
| * @param {ASTNode} node The root node to traverse. | |
| * @param {Object} options The option object. | |
| * @param {Object} [options.visitorKeys=DEFAULT_VISITOR_KEYS] The keys of each node types to traverse child nodes. Default is `./default-visitor-keys.json`. | |
| * @param {Function} [options.enter=noop] The callback function which is called on entering each node. | |
| * @param {Function} [options.leave=noop] The callback function which is called on leaving each node. | |
| * @returns {void} | |
| */ | |
| traverse(node, options) { | |
| this._current = null; | |
| this._parents = []; | |
| this._skipped = false; | |
| this._broken = false; | |
| this._visitorKeys = options.visitorKeys || vk.KEYS; | |
| this._enter = options.enter || noop; | |
| this._leave = options.leave || noop; | |
| this._traverse(node, null); | |
| } | |
| /** | |
| * Traverse the given AST tree recursively. | |
| * @param {ASTNode} node The current node. | |
| * @param {ASTNode|null} parent The parent node. | |
| * @returns {void} | |
| * @private | |
| */ | |
| _traverse(node, parent) { | |
| if (!isNode(node)) { | |
| return; | |
| } | |
| this._current = node; | |
| this._skipped = false; | |
| this._enter(node, parent); | |
| if (!this._skipped && !this._broken) { | |
| const keys = getVisitorKeys(this._visitorKeys, node); | |
| if (keys.length >= 1) { | |
| this._parents.push(node); | |
| for (let i = 0; i < keys.length && !this._broken; ++i) { | |
| const child = node[keys[i]]; | |
| if (Array.isArray(child)) { | |
| for ( | |
| let j = 0; | |
| j < child.length && !this._broken; | |
| ++j | |
| ) { | |
| this._traverse(child[j], node); | |
| } | |
| } else { | |
| this._traverse(child, node); | |
| } | |
| } | |
| this._parents.pop(); | |
| } | |
| } | |
| if (!this._broken) { | |
| this._leave(node, parent); | |
| } | |
| this._current = parent; | |
| } | |
| /** | |
| * Calculates the keys to use for traversal. | |
| * @param {ASTNode} node The node to read keys from. | |
| * @returns {string[]} An array of keys to visit on the node. | |
| * @private | |
| */ | |
| static getKeys(node) { | |
| return vk.getKeys(node); | |
| } | |
| /** | |
| * Traverse the given AST tree. | |
| * @param {ASTNode} node The root node to traverse. | |
| * @param {Object} options The option object. | |
| * @param {Object} [options.visitorKeys=DEFAULT_VISITOR_KEYS] The keys of each node types to traverse child nodes. Default is `./default-visitor-keys.json`. | |
| * @param {Function} [options.enter=noop] The callback function which is called on entering each node. | |
| * @param {Function} [options.leave=noop] The callback function which is called on leaving each node. | |
| * @returns {void} | |
| */ | |
| static traverse(node, options) { | |
| new Traverser().traverse(node, options); | |
| } | |
| /** | |
| * The default visitor keys. | |
| * @type {Object} | |
| */ | |
| static get DEFAULT_VISITOR_KEYS() { | |
| return vk.KEYS; | |
| } | |
| } | |
| module.exports = Traverser; | |