convert-ast.js
2.77 KB
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertAst = void 0;
const ts = require("typescript");
const util_1 = require("./util");
/**
* Takes a `ts.SourceFile` and creates data structures that are easier (or more performant) to traverse.
* Note that there is only a performance gain if you can reuse these structures. It's not recommended for one-time AST walks.
*/
function convertAst(sourceFile) {
const wrapped = {
node: sourceFile,
parent: undefined,
kind: ts.SyntaxKind.SourceFile,
children: [],
next: undefined,
skip: undefined,
};
const flat = [];
let current = wrapped;
function collectChildren(node) {
current.children.push({
node,
parent: current,
kind: node.kind,
children: [],
next: undefined,
skip: undefined,
});
}
const stack = [];
while (true) {
if (current.children.length === 0) {
ts.forEachChild(current.node, collectChildren);
if (current.children.length === 0) {
current = current.parent; // nothing to do here, go back to parent
}
else {
// recurse into first child
const firstChild = current.children[0];
current.next = firstChild;
flat.push(firstChild.node);
if (util_1.isNodeKind(firstChild.kind))
current = firstChild;
stack.push(1); // set index in stack so we know where to continue processing children
}
}
else {
const index = stack[stack.length - 1];
if (index < current.children.length) { // handles 2nd child to the last
const currentChild = current.children[index];
flat.push(currentChild.node);
let previous = current.children[index - 1];
while (previous.children.length !== 0) {
previous.skip = currentChild;
previous = previous.children[previous.children.length - 1];
}
previous.skip = previous.next = currentChild;
++stack[stack.length - 1];
if (util_1.isNodeKind(currentChild.kind))
current = currentChild; // recurse into child
}
else {
// done on this node
if (stack.length === 1)
break;
// remove index from stack and go back to parent
stack.pop();
current = current.parent;
}
}
}
return {
wrapped,
flat,
};
}
exports.convertAst = convertAst;
//# sourceMappingURL=convert-ast.js.map