Add a new utility for traversing the AST.

This commit is contained in:
Logan Smyth 2017-10-11 23:03:14 -04:00
parent ef185feb35
commit 191624d800
4 changed files with 126 additions and 31 deletions

View File

@ -1732,4 +1732,15 @@ declare module "babel-types" {
declare function cloneDeep<T>(n: T): T; declare function cloneDeep<T>(n: T): T;
declare function removeProperties<T>(n: T, opts: ?{}): void; declare function removeProperties<T>(n: T, opts: ?{}): void;
declare function removePropertiesDeep<T>(n: T, opts: ?{}): T; declare function removePropertiesDeep<T>(n: T, opts: ?{}): T;
declare type TraversalAncestors = Array<{
node: BabelNode,
key: string,
index?: number,
}>;
declare type TraversalHandler<T> = (BabelNode, TraversalAncestors, T) => void;
declare type TraversalHandlers<T> = {
enter?: TraversalHandler<T>,
exit?: TraversalHandler<T>,
};
declare function traverse<T>(n: BabelNode, TraversalHandler<T> | TraversalHandlers<T>, state?: T): void;
} }

View File

@ -66,6 +66,9 @@ export { VISITOR_KEYS, ALIAS_KEYS, NODE_FIELDS, BUILDER_KEYS, DEPRECATED_KEYS };
import * as _react from "./react"; import * as _react from "./react";
export { _react as react }; export { _react as react };
import { traverse, traverseFast } from "./traverse";
export { traverse, traverseFast };
/** /**
* Registers `is[Type]` and `assert[Type]` for all types. * Registers `is[Type]` and `assert[Type]` for all types.
*/ */
@ -534,36 +537,6 @@ export function isNode(node?): boolean {
toFastProperties(t); toFastProperties(t);
toFastProperties(t.VISITOR_KEYS); toFastProperties(t.VISITOR_KEYS);
/**
* A prefix AST traversal implementation implementation.
*/
export function traverseFast(
node: Node,
enter: (node: Node) => void,
opts?: Object,
) {
if (!node) return;
const keys = t.VISITOR_KEYS[node.type];
if (!keys) return;
opts = opts || {};
enter(node, opts);
for (const key of keys) {
const subNode = node[key];
if (Array.isArray(subNode)) {
for (const node of subNode) {
traverseFast(node, enter, opts);
}
} else {
traverseFast(subNode, enter, opts);
}
}
}
const CLEAR_KEYS: Array = ["tokens", "start", "end", "loc", "raw", "rawValue"]; const CLEAR_KEYS: Array = ["tokens", "start", "end", "loc", "raw", "rawValue"];
const CLEAR_KEYS_PLUS_COMMENTS: Array = t.COMMENT_KEYS const CLEAR_KEYS_PLUS_COMMENTS: Array = t.COMMENT_KEYS

View File

@ -0,0 +1,99 @@
import { VISITOR_KEYS } from "./index";
/**
* A prefix AST traversal implementation meant for simple searching
* and processing.
*/
export function traverseFast(
node: Node,
enter: (node: Node) => void,
opts?: Object,
) {
if (!node) return;
const keys = VISITOR_KEYS[node.type];
if (!keys) return;
opts = opts || {};
enter(node, opts);
for (const key of keys) {
const subNode = node[key];
if (Array.isArray(subNode)) {
for (const node of subNode) {
traverseFast(node, enter, opts);
}
} else {
traverseFast(subNode, enter, opts);
}
}
}
export type TraversalAncestors = Array<{
node: BabelNode,
key: string,
index?: number,
}>;
export type TraversalHandler<T> = (BabelNode, TraversalAncestors, T) => void;
export type TraversalHandlers<T> = {
enter?: TraversalHandler<T>,
exit?: TraversalHandler<T>,
};
/**
* A general AST traversal with both prefix and postfix handlers, and a
* state object. Exposes ancestry data to each handler so that more complex
* AST data can be taken into account.
*/
export function traverse<T>(
node: BabelNode,
handlers: TraversalHandler<T> | TraversalHandlers<T>,
state?: T,
) {
if (typeof handlers === "function") {
handlers = { enter: handlers };
}
const { enter, exit } = handlers;
traverseSimpleImpl(node, enter, exit, state, []);
}
function traverseSimpleImpl(node, enter, exit, state, ancestors) {
const keys = VISITOR_KEYS[node.type];
if (!keys) return;
if (enter) enter(node, ancestors, state);
for (const key of keys) {
const subNode = node[key];
if (Array.isArray(subNode)) {
for (let i = 0; i < subNode.length; i++) {
const child = subNode[i];
if (!child) continue;
ancestors.push({
node,
key,
index: i,
});
traverseSimpleImpl(child, enter, exit, state, ancestors);
ancestors.pop();
}
} else if (subNode) {
ancestors.push({
node,
key,
});
traverseSimpleImpl(subNode, enter, exit, state, ancestors);
ancestors.pop();
}
}
if (exit) exit(node, ancestors, state);
}

View File

@ -140,7 +140,19 @@ lines.push(
`declare function clone<T>(n: T): T;`, `declare function clone<T>(n: T): T;`,
`declare function cloneDeep<T>(n: T): T;`, `declare function cloneDeep<T>(n: T): T;`,
`declare function removeProperties<T>(n: T, opts: ?{}): void;`, `declare function removeProperties<T>(n: T, opts: ?{}): void;`,
`declare function removePropertiesDeep<T>(n: T, opts: ?{}): T;` `declare function removePropertiesDeep<T>(n: T, opts: ?{}): T;`,
`declare type TraversalAncestors = Array<{
node: BabelNode,
key: string,
index?: number,
}>;
declare type TraversalHandler<T> = (BabelNode, TraversalAncestors, T) => void;
declare type TraversalHandlers<T> = {
enter?: TraversalHandler<T>,
exit?: TraversalHandler<T>,
};`.replace(/(^|\n) {2}/g, "$1"),
// eslint-disable-next-line
`declare function traverse<T>(n: BabelNode, TraversalHandler<T> | TraversalHandlers<T>, state?: T): void;`
); );
for (const type in t.FLIPPED_ALIAS_KEYS) { for (const type in t.FLIPPED_ALIAS_KEYS) {