[d565449] | 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;
|
---|