/
index.ts
50 lines (42 loc) 路 1.44 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import { VISITOR_KEYS } from "@babel/types";
export default function checkDuplicateNodes(ast) {
if (arguments.length !== 1) {
throw new Error("checkDuplicateNodes accepts only one argument: ast");
}
// A Map from node to its parent
const parentsMap = new Map();
const hidePrivateProperties = (key, val) => {
// Hides properties like _shadowedFunctionLiteral,
// which makes the AST circular
if (key[0] === "_") return "[Private]";
return val;
};
const stack = [{ node: ast, parent: null }];
let item;
while ((item = stack.pop()) !== undefined) {
const { node, parent } = item;
if (!node) continue;
const keys = VISITOR_KEYS[node.type];
if (!keys) continue;
if (parentsMap.has(node)) {
const parents = [parentsMap.get(node), parent];
throw new Error(
"Do not reuse nodes. Use `t.cloneNode` (or `t.clone`/`t.cloneDeep` if using babel@6) to copy them.\n" +
JSON.stringify(node, hidePrivateProperties, 2) +
"\nParent:\n" +
JSON.stringify(parents, hidePrivateProperties, 2),
);
}
parentsMap.set(node, parent);
for (const key of keys) {
const subNode = node[key];
if (Array.isArray(subNode)) {
for (const child of subNode) {
stack.push({ node: child, parent: node });
}
} else if (typeof subNode === "object" && subNode !== null) {
stack.push({ node: subNode, parent: node });
}
}
}
}