Files
babel/packages/babel-traverse/src/index.ts
Bogdan Savluk d98418efbe Convert @babel/traverse to TypeScript (#12488)
Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>
2021-01-24 01:33:09 +01:00

146 lines
3.0 KiB
TypeScript

import TraversalContext from "./context";
import * as visitors from "./visitors";
import * as t from "@babel/types";
import * as cache from "./cache";
import type NodePath from "./path";
import type Scope from "./scope";
import type { Visitor } from "./types";
export type { Visitor };
export { default as NodePath } from "./path";
export { default as Scope } from "./scope";
export { default as Hub } from "./hub";
export type { HubInterface } from "./hub";
export { visitors };
export type TraverseOptions<S = t.Node> =
| {
scope?: Scope;
noScope?: boolean;
denylist?: string[];
}
| Visitor<S>;
function traverse<S>(
parent: t.Node,
opts: TraverseOptions<S>,
scope: Scope | undefined,
state: S,
parentPath?: NodePath,
): void;
function traverse(
parent: t.Node,
opts: TraverseOptions,
scope?: Scope,
state?: any,
parentPath?: NodePath,
): void;
function traverse(
parent: t.Node,
opts: TraverseOptions = {},
scope?: Scope,
state?: any,
parentPath?: NodePath,
) {
if (!parent) return;
if (!opts.noScope && !scope) {
if (parent.type !== "Program" && parent.type !== "File") {
throw new Error(
"You must pass a scope and parentPath unless traversing a Program/File. " +
`Instead of that you tried to traverse a ${parent.type} node without ` +
"passing scope and parentPath.",
);
}
}
if (!t.VISITOR_KEYS[parent.type]) {
return;
}
visitors.explode(opts);
traverse.node(parent, opts, scope, state, parentPath);
}
export default traverse;
traverse.visitors = visitors;
traverse.verify = visitors.verify;
traverse.explode = visitors.explode;
traverse.cheap = function (node, enter) {
return t.traverseFast(node, enter);
};
traverse.node = function (
node: t.Node,
opts: TraverseOptions,
scope?: Scope,
state?: any,
parentPath?: NodePath,
skipKeys?,
) {
const keys = t.VISITOR_KEYS[node.type];
if (!keys) return;
const context = new TraversalContext(scope, opts, state, parentPath);
for (const key of keys) {
if (skipKeys && skipKeys[key]) continue;
if (context.visit(node, key)) return;
}
};
traverse.clearNode = function (node: t.Node, opts?) {
t.removeProperties(node, opts);
cache.path.delete(node);
};
traverse.removeProperties = function (tree, opts?) {
t.traverseFast(tree, traverse.clearNode, opts);
return tree;
};
function hasDenylistedType(path: NodePath, state) {
if (path.node.type === state.type) {
state.has = true;
path.stop();
}
}
traverse.hasType = function (
tree: any,
type: any,
denylistTypes?: Array<string>,
): boolean {
// the node we're searching in is denylisted
if (denylistTypes?.includes(tree.type)) return false;
// the type we're looking for is the same as the passed node
if (tree.type === type) return true;
const state = {
has: false,
type: type,
};
traverse(
tree,
{
noScope: true,
denylist: denylistTypes,
enter: hasDenylistedType,
},
null,
state,
);
return state.has;
};
traverse.cache = cache;