| /** | 
|  * @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; | 
|     } | 
|   | 
|     /** | 
|      * @returns {ASTNode} The current node. | 
|      */ | 
|     current() { | 
|         return this._current; | 
|     } | 
|   | 
|     /** | 
|      * @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; |