/
collect.js
125 lines (111 loc) 路 3.78 KB
/
collect.js
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// @flow
import type {TSModuleGraph} from './TSModuleGraph';
import nullthrows from 'nullthrows';
import ts from 'typescript';
import {TSModule} from './TSModule';
import {getExportedName, isDeclaration} from './utils';
export function collect(
moduleGraph: TSModuleGraph,
context: any,
sourceFile: any,
): any {
// Factory only exists on TS >= 4.0
const {factory = ts} = context;
// When module definitions are nested inside each other (e.g with module augmentation),
// we want to keep track of the hierarchy so we can associated nodes with the right module.
const moduleStack: Array<?TSModule> = [];
let _currentModule: ?TSModule;
let visit = (node: any): any => {
if (ts.isBundle(node)) {
return factory.updateBundle(node, ts.visitNodes(node.sourceFiles, visit));
}
if (ts.isModuleDeclaration(node)) {
moduleStack.push(_currentModule);
_currentModule = new TSModule();
moduleGraph.addModule(node.name.text, _currentModule);
}
if (!_currentModule) {
return ts.visitEachChild(node, visit, context);
}
let currentModule = nullthrows(_currentModule);
if (ts.isImportDeclaration(node) && node.importClause) {
if (node.importClause.namedBindings) {
if (node.importClause.namedBindings.elements) {
for (let element of node.importClause.namedBindings.elements) {
currentModule.addImport(
element.name.text,
node.moduleSpecifier.text,
(element.propertyName ?? element.name).text,
);
}
} else if (node.importClause.namedBindings.name) {
currentModule.addImport(
node.importClause.namedBindings.name.text,
node.moduleSpecifier.text,
'*',
);
}
}
if (node.importClause.name) {
currentModule.addImport(
node.importClause.name.text,
node.moduleSpecifier.text,
'default',
);
}
}
if (ts.isExportDeclaration(node)) {
if (node.exportClause) {
for (let element of node.exportClause.elements) {
if (node.moduleSpecifier) {
currentModule.addExport(
element.name.text,
(element.propertyName ?? element.name).text,
node.moduleSpecifier.text,
);
} else {
currentModule.addExport(
element.name.text,
(element.propertyName ?? element.name).text,
);
}
}
} else {
currentModule.addWildcardExport(node.moduleSpecifier.text);
}
}
// Handle `export default name;`
if (ts.isExportAssignment(node) && ts.isIdentifier(node.expression)) {
currentModule.addExport('default', node.expression.text);
}
if (isDeclaration(node)) {
if (node.name) {
currentModule.addLocal(node.name.text, node);
}
let name = getExportedName(node);
if (name) {
currentModule.addLocal(name, node);
currentModule.addExport(name, name);
}
}
if (ts.isVariableStatement(node) && node.modifiers) {
let isExported = node.modifiers.some(
m => m.kind === ts.SyntaxKind.ExportKeyword,
);
for (let v of node.declarationList.declarations) {
currentModule.addLocal(v.name.text, v);
if (isExported) {
currentModule.addExport(v.name.text, v.name.text);
}
}
}
const results = ts.visitEachChild(node, visit, context);
// After we finish traversing the children of a module definition,
// we need to make sure that subsequent nodes get associated with the next-highest level module.
if (ts.isModuleDeclaration(node)) {
_currentModule = moduleStack.pop();
}
return results;
};
return ts.visitNode(sourceFile, visit);
}