| /** | |
|  * @fileoverview Traverser to traverse AST trees. | |
|  * @author Nicholas C. Zakas | |
|  * @author Toru Nagashima | |
|  */ | |
| "use strict"; | |
| 
 | |
| //------------------------------------------------------------------------------ | |
| // 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;
 |