@atlaspack/transformer-typescript-types 2.12.1-canary.3354
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/lib/TSModule.js +53 -0
- package/lib/TSModuleGraph.js +252 -0
- package/lib/TSTypesTransformer.js +205 -0
- package/lib/collect.js +128 -0
- package/lib/shake.js +226 -0
- package/lib/utils.js +30 -0
- package/lib/wrappers.js +72 -0
- package/package.json +33 -0
- package/src/TSModule.js +55 -0
- package/src/TSModuleGraph.js +277 -0
- package/src/TSTypesTransformer.js +185 -0
- package/src/collect.js +157 -0
- package/src/shake.js +318 -0
- package/src/utils.js +28 -0
- package/src/wrappers.js +202 -0
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atlaspack/transformer-typescript-types",
|
|
3
|
+
"version": "2.12.1-canary.3354+7bb54d46a",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/atlassian-labs/atlaspack.git"
|
|
11
|
+
},
|
|
12
|
+
"main": "lib/TSTypesTransformer.js",
|
|
13
|
+
"source": "src/TSTypesTransformer.js",
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">= 16.0.0",
|
|
16
|
+
"atlaspack": "2.12.1-canary.3354+7bb54d46a"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@atlaspack/diagnostic": "2.12.1-canary.3354+7bb54d46a",
|
|
20
|
+
"@atlaspack/plugin": "2.12.1-canary.3354+7bb54d46a",
|
|
21
|
+
"@atlaspack/ts-utils": "2.12.1-canary.3354+7bb54d46a",
|
|
22
|
+
"@atlaspack/utils": "2.12.1-canary.3354+7bb54d46a",
|
|
23
|
+
"@parcel/source-map": "^2.1.1",
|
|
24
|
+
"nullthrows": "^1.1.1"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"typescript": ">=3.0.0"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"typescript": ">=3.0.0"
|
|
31
|
+
},
|
|
32
|
+
"gitHead": "7bb54d46a00c5ba9cdbc2ee426dcbe82c8d79a3e"
|
|
33
|
+
}
|
package/src/TSModule.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
export type Import = {|specifier: string, imported: string|};
|
|
4
|
+
export type Export =
|
|
5
|
+
| {|name: string, imported: string, specifier?: ?string|}
|
|
6
|
+
| {|specifier: string|};
|
|
7
|
+
|
|
8
|
+
export class TSModule {
|
|
9
|
+
imports: Map<string, Import>;
|
|
10
|
+
exports: Array<Export>;
|
|
11
|
+
bindings: Map<string, Set<any>>;
|
|
12
|
+
names: Map<string, string>;
|
|
13
|
+
used: Set<string>;
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
this.imports = new Map();
|
|
17
|
+
this.exports = [];
|
|
18
|
+
this.bindings = new Map();
|
|
19
|
+
this.names = new Map();
|
|
20
|
+
this.used = new Set();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
addImport(local: string, specifier: string, imported: string) {
|
|
24
|
+
this.imports.set(local, {specifier, imported});
|
|
25
|
+
if (imported !== '*' && imported !== 'default') {
|
|
26
|
+
this.names.set(local, local);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// if not a reexport: imported = local, name = exported
|
|
31
|
+
addExport(name: string, imported: string, specifier: ?string) {
|
|
32
|
+
this.exports.push({name, specifier, imported});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
addWildcardExport(specifier: string) {
|
|
36
|
+
this.exports.push({specifier});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
addLocal(name: string, node: any) {
|
|
40
|
+
const bindings = this.bindings.get(name) ?? new Set();
|
|
41
|
+
bindings.add(node);
|
|
42
|
+
this.bindings.set(name, bindings);
|
|
43
|
+
if (name !== 'default') {
|
|
44
|
+
this.names.set(name, name);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getName(name: string): string {
|
|
49
|
+
return this.names.get(name) || name;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
hasBinding(name: string): boolean {
|
|
53
|
+
return this.bindings.has(name);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import type {TSModule, Export} from './TSModule';
|
|
3
|
+
|
|
4
|
+
import nullthrows from 'nullthrows';
|
|
5
|
+
import invariant from 'assert';
|
|
6
|
+
import ts from 'typescript';
|
|
7
|
+
|
|
8
|
+
export class TSModuleGraph {
|
|
9
|
+
modules: Map<string, TSModule>;
|
|
10
|
+
mainModuleName: string;
|
|
11
|
+
mainModule: ?TSModule;
|
|
12
|
+
syntheticImportCount: number;
|
|
13
|
+
|
|
14
|
+
constructor(mainModuleName: string) {
|
|
15
|
+
this.modules = new Map();
|
|
16
|
+
this.mainModuleName = mainModuleName;
|
|
17
|
+
this.mainModule = null;
|
|
18
|
+
this.syntheticImportCount = 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
addModule(name: string, module: TSModule) {
|
|
22
|
+
this.modules.set(name, module);
|
|
23
|
+
if (name === this.mainModuleName) {
|
|
24
|
+
this.mainModule = module;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getModule(name: string): ?TSModule {
|
|
29
|
+
return this.modules.get(name);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
markUsed(module: TSModule, name: string, context: any): void {
|
|
33
|
+
// If name is imported, mark used in the original module
|
|
34
|
+
if (module.imports.has(name)) {
|
|
35
|
+
module.used.add(name);
|
|
36
|
+
let resolved = this.resolveImport(module, name);
|
|
37
|
+
// Missing or external
|
|
38
|
+
if (!resolved || resolved.module === module) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return this.markUsed(resolved.module, resolved.imported, context);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (module.used.has(name)) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.used.add(name);
|
|
50
|
+
|
|
51
|
+
// Visit all child nodes of the original binding and mark any referenced types as used.
|
|
52
|
+
let visit = (node: any) => {
|
|
53
|
+
if (ts.isQualifiedName(node) && ts.isIdentifier(node.left)) {
|
|
54
|
+
let resolved = this.resolveImport(
|
|
55
|
+
module,
|
|
56
|
+
node.left.text,
|
|
57
|
+
node.right.text,
|
|
58
|
+
);
|
|
59
|
+
if (resolved) {
|
|
60
|
+
this.markUsed(resolved.module, resolved.imported, context);
|
|
61
|
+
}
|
|
62
|
+
} else if (ts.isIdentifier(node)) {
|
|
63
|
+
this.markUsed(module, node.text, context);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return ts.visitEachChild(node, visit, context);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
let bindings = module.bindings.get(name);
|
|
70
|
+
if (bindings) {
|
|
71
|
+
for (let node of bindings) {
|
|
72
|
+
ts.visitEachChild(node, visit, context);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getExport(
|
|
78
|
+
m: TSModule,
|
|
79
|
+
e: Export,
|
|
80
|
+
): ?{|imported: string, module: TSModule, name: string|} {
|
|
81
|
+
invariant(e.name != null);
|
|
82
|
+
let exportName = e.name;
|
|
83
|
+
|
|
84
|
+
// Re-export
|
|
85
|
+
if (e.specifier && e.imported) {
|
|
86
|
+
let m = this.getModule(e.specifier);
|
|
87
|
+
if (!m) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let exp = this.resolveExport(m, e.imported);
|
|
92
|
+
if (!exp) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
module: exp.module,
|
|
98
|
+
imported: exp.imported || exp.name,
|
|
99
|
+
name: exportName,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Import and then export
|
|
104
|
+
if (m.imports.has(exportName)) {
|
|
105
|
+
let imp = this.resolveImport(m, exportName);
|
|
106
|
+
if (!imp) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {module: imp.module, imported: imp.name, name: exportName};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Named export
|
|
114
|
+
return {
|
|
115
|
+
module: m,
|
|
116
|
+
name: exportName,
|
|
117
|
+
imported: e.imported != null ? m.getName(e.imported) : exportName,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
resolveImport(
|
|
122
|
+
module: TSModule,
|
|
123
|
+
local: string,
|
|
124
|
+
imported?: string,
|
|
125
|
+
): ?{|imported: string, module: TSModule, name: string|} {
|
|
126
|
+
let i = module.imports.get(local);
|
|
127
|
+
if (!i) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let m = this.getModule(i.specifier);
|
|
132
|
+
if (!m) {
|
|
133
|
+
// External module. pass through the import.
|
|
134
|
+
return {module, name: local, imported: imported || i.imported};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return this.resolveExport(m, imported || i.imported);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
resolveExport(
|
|
141
|
+
module: TSModule,
|
|
142
|
+
name: string,
|
|
143
|
+
): ?{|imported: string, module: TSModule, name: string|} {
|
|
144
|
+
for (let e of module.exports) {
|
|
145
|
+
if (e.name === name) {
|
|
146
|
+
return this.getExport(module, e);
|
|
147
|
+
} else if (e.specifier) {
|
|
148
|
+
const m = this.resolveExport(
|
|
149
|
+
nullthrows(this.getModule(e.specifier)),
|
|
150
|
+
name,
|
|
151
|
+
);
|
|
152
|
+
if (m) {
|
|
153
|
+
return m;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
getAllExports(
|
|
160
|
+
module: TSModule = nullthrows(this.mainModule),
|
|
161
|
+
excludeDefault: boolean = false,
|
|
162
|
+
): Array<{|imported: string, module: TSModule, name: string|}> {
|
|
163
|
+
let res = [];
|
|
164
|
+
for (let e of module.exports) {
|
|
165
|
+
if (e.name && (!excludeDefault || e.name !== 'default')) {
|
|
166
|
+
let exp = this.getExport(module, e);
|
|
167
|
+
if (exp) {
|
|
168
|
+
res.push(exp);
|
|
169
|
+
}
|
|
170
|
+
} else if (e.specifier) {
|
|
171
|
+
let m = this.getModule(e.specifier);
|
|
172
|
+
if (m) {
|
|
173
|
+
res.push(...this.getAllExports(m, true));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return res;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
getAllImports(): Map<string, Map<string, string>> {
|
|
181
|
+
// Build a map of all imports for external modules
|
|
182
|
+
let importsBySpecifier: Map<string, Map<string, string>> = new Map();
|
|
183
|
+
for (let module of this.modules.values()) {
|
|
184
|
+
for (let [name, imp] of module.imports) {
|
|
185
|
+
if (module.used.has(name) && !this.modules.has(imp.specifier)) {
|
|
186
|
+
let importMap = importsBySpecifier.get(imp.specifier);
|
|
187
|
+
if (!importMap) {
|
|
188
|
+
importMap = new Map();
|
|
189
|
+
importsBySpecifier.set(imp.specifier, importMap);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
name = module.getName(name);
|
|
193
|
+
importMap.set(name, imp.imported);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return importsBySpecifier;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
propagate(context: any): Map<string, TSModule> {
|
|
202
|
+
// Resolve all exported values, and mark them as used.
|
|
203
|
+
let names = Object.create(null);
|
|
204
|
+
let exportedNames = new Map<string, TSModule>();
|
|
205
|
+
for (let e of this.getAllExports()) {
|
|
206
|
+
this.markUsed(e.module, e.imported, context);
|
|
207
|
+
e.module.names.set(e.imported, e.name);
|
|
208
|
+
names[e.name] = 1;
|
|
209
|
+
exportedNames.set(e.name, e.module);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let importedSymbolsToUpdate = [];
|
|
213
|
+
|
|
214
|
+
// Assign unique names across all modules
|
|
215
|
+
for (let m of this.modules.values()) {
|
|
216
|
+
for (let [orig, name] of m.names) {
|
|
217
|
+
if (exportedNames.has(name) && exportedNames.get(name) === m) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (!m.used.has(orig)) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (m.imports.has(orig)) {
|
|
226
|
+
// Update imports after all modules's local variables have been renamed
|
|
227
|
+
importedSymbolsToUpdate.push([m, orig]);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (names[name]) {
|
|
232
|
+
m.names.set(name, `_${name}${names[name]++}`);
|
|
233
|
+
} else {
|
|
234
|
+
names[name] = 1;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Map of imported specifiers -> map of imported names to local names
|
|
240
|
+
let imports = new Map();
|
|
241
|
+
|
|
242
|
+
for (let [m, orig] of importedSymbolsToUpdate) {
|
|
243
|
+
let imp = nullthrows(m.imports.get(orig));
|
|
244
|
+
let imported = nullthrows(this.resolveImport(m, orig));
|
|
245
|
+
|
|
246
|
+
// If the module is bundled, map the local name to the original exported name.
|
|
247
|
+
if (this.modules.has(imp.specifier)) {
|
|
248
|
+
m.names.set(orig, imported.imported);
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// If it's external, then we need to dedup duplicate imported names, and ensure
|
|
253
|
+
// that they do not conflict with any exported or local names.
|
|
254
|
+
let importedNames = imports.get(imp.specifier);
|
|
255
|
+
if (!importedNames) {
|
|
256
|
+
importedNames = new Map();
|
|
257
|
+
imports.set(imp.specifier, importedNames);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
let name = importedNames.get(imported.imported);
|
|
261
|
+
if (!name) {
|
|
262
|
+
if (names[imported.imported]) {
|
|
263
|
+
name = `_${imported.imported}${names[imported.imported]++}`;
|
|
264
|
+
} else {
|
|
265
|
+
name = imported.imported;
|
|
266
|
+
names[imported.imported] = 1;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
importedNames.set(imported.imported, name);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
m.names.set(orig, name);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return exportedNames;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// @flow strict-local
|
|
2
|
+
|
|
3
|
+
import {Transformer} from '@atlaspack/plugin';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import SourceMap from '@parcel/source-map';
|
|
6
|
+
import type {DiagnosticCodeFrame} from '@atlaspack/diagnostic';
|
|
7
|
+
import type {CompilerOptions} from 'typescript';
|
|
8
|
+
|
|
9
|
+
import ts from 'typescript';
|
|
10
|
+
import {CompilerHost, loadTSConfig} from '@atlaspack/ts-utils';
|
|
11
|
+
import {normalizeSeparators} from '@atlaspack/utils';
|
|
12
|
+
import ThrowableDiagnostic, {escapeMarkdown} from '@atlaspack/diagnostic';
|
|
13
|
+
import {TSModuleGraph} from './TSModuleGraph';
|
|
14
|
+
import nullthrows from 'nullthrows';
|
|
15
|
+
import {collect} from './collect';
|
|
16
|
+
import {shake} from './shake';
|
|
17
|
+
|
|
18
|
+
export default (new Transformer({
|
|
19
|
+
loadConfig({config, options}) {
|
|
20
|
+
return loadTSConfig(config, options);
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
transform({asset, config, options, logger}) {
|
|
24
|
+
let opts: CompilerOptions = {
|
|
25
|
+
// React is the default. Users can override this by supplying their own tsconfig,
|
|
26
|
+
// which many TypeScript users will already have for typechecking, etc.
|
|
27
|
+
jsx: ts.JsxEmit.React,
|
|
28
|
+
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
|
29
|
+
...config,
|
|
30
|
+
// Always emit output
|
|
31
|
+
noEmit: false,
|
|
32
|
+
noEmitOnError: false,
|
|
33
|
+
declaration: true,
|
|
34
|
+
declarationMap: true,
|
|
35
|
+
isolatedModules: false,
|
|
36
|
+
emitDeclarationOnly: true,
|
|
37
|
+
outFile: 'index.d.ts',
|
|
38
|
+
// createProgram doesn't support incremental mode
|
|
39
|
+
composite: false,
|
|
40
|
+
incremental: false,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
let host = new CompilerHost(options.inputFS, ts, logger);
|
|
44
|
+
// $FlowFixMe
|
|
45
|
+
let program = ts.createProgram([asset.filePath], opts, host);
|
|
46
|
+
|
|
47
|
+
for (let file of program.getSourceFiles()) {
|
|
48
|
+
if (path.normalize(file.fileName) !== asset.filePath) {
|
|
49
|
+
asset.invalidateOnFileChange(
|
|
50
|
+
host.redirectTypes.get(file.fileName) ?? file.fileName,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let mainModuleName = normalizeSeparators(
|
|
56
|
+
path
|
|
57
|
+
.relative(program.getCommonSourceDirectory(), asset.filePath)
|
|
58
|
+
.slice(0, -path.extname(asset.filePath).length),
|
|
59
|
+
);
|
|
60
|
+
let moduleGraph = new TSModuleGraph(mainModuleName);
|
|
61
|
+
|
|
62
|
+
let emitResult = program.emit(undefined, undefined, undefined, true, {
|
|
63
|
+
afterDeclarations: [
|
|
64
|
+
// 1. Build module graph
|
|
65
|
+
context => sourceFile => {
|
|
66
|
+
return collect(moduleGraph, context, sourceFile);
|
|
67
|
+
},
|
|
68
|
+
// 2. Tree shake and rename types
|
|
69
|
+
context => sourceFile => {
|
|
70
|
+
return shake(moduleGraph, context, sourceFile);
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
let diagnostics = ts
|
|
76
|
+
.getPreEmitDiagnostics(program)
|
|
77
|
+
.concat(emitResult.diagnostics);
|
|
78
|
+
|
|
79
|
+
let diagnosticIds = new Set();
|
|
80
|
+
let deduplicatedDiagnostics = [];
|
|
81
|
+
for (let d of diagnostics) {
|
|
82
|
+
if (d.start != null && d.length != null && d.messageText != null) {
|
|
83
|
+
let id = `${d.start}:${d.length}:${ts.flattenDiagnosticMessageText(
|
|
84
|
+
d.messageText,
|
|
85
|
+
'\n',
|
|
86
|
+
)}`;
|
|
87
|
+
if (!diagnosticIds.has(id)) {
|
|
88
|
+
deduplicatedDiagnostics.push(d);
|
|
89
|
+
}
|
|
90
|
+
diagnosticIds.add(id);
|
|
91
|
+
} else {
|
|
92
|
+
deduplicatedDiagnostics.push(d);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let atlaspackDiagnostics = deduplicatedDiagnostics.map(diagnostic => {
|
|
97
|
+
let filename = asset.filePath;
|
|
98
|
+
let {file} = diagnostic;
|
|
99
|
+
|
|
100
|
+
let diagnosticMessage = ts.flattenDiagnosticMessageText(
|
|
101
|
+
diagnostic.messageText,
|
|
102
|
+
'\n',
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
let codeframe: ?DiagnosticCodeFrame;
|
|
106
|
+
if (file != null && diagnostic.start != null) {
|
|
107
|
+
let source = file.text || diagnostic.source;
|
|
108
|
+
if (file.fileName) {
|
|
109
|
+
filename = file.fileName;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// $FlowFixMe
|
|
113
|
+
if (source) {
|
|
114
|
+
let lineChar = file.getLineAndCharacterOfPosition(diagnostic.start);
|
|
115
|
+
let start = {
|
|
116
|
+
line: lineChar.line + 1,
|
|
117
|
+
column: lineChar.character + 1,
|
|
118
|
+
};
|
|
119
|
+
let end = {
|
|
120
|
+
line: start.line,
|
|
121
|
+
column: start.column + 1,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
if (
|
|
125
|
+
typeof diagnostic.start === 'number' &&
|
|
126
|
+
typeof diagnostic.length === 'number'
|
|
127
|
+
) {
|
|
128
|
+
let endCharPosition = file.getLineAndCharacterOfPosition(
|
|
129
|
+
diagnostic.start + diagnostic.length,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
end = {
|
|
133
|
+
line: endCharPosition.line + 1,
|
|
134
|
+
column: endCharPosition.character,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
codeframe = {
|
|
139
|
+
filePath: filename,
|
|
140
|
+
code: source,
|
|
141
|
+
codeHighlights: [
|
|
142
|
+
{
|
|
143
|
+
start,
|
|
144
|
+
end,
|
|
145
|
+
message: escapeMarkdown(diagnosticMessage),
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
message: escapeMarkdown(diagnosticMessage),
|
|
154
|
+
codeFrames: codeframe ? [codeframe] : undefined,
|
|
155
|
+
};
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
if (host.outputCode == null) {
|
|
159
|
+
throw new ThrowableDiagnostic({diagnostic: atlaspackDiagnostics});
|
|
160
|
+
} else {
|
|
161
|
+
for (let d of atlaspackDiagnostics) {
|
|
162
|
+
logger.warn(d);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let code = nullthrows(host.outputCode);
|
|
167
|
+
code = code.substring(0, code.lastIndexOf('//# sourceMappingURL'));
|
|
168
|
+
|
|
169
|
+
let map = JSON.parse(nullthrows(host.outputMap));
|
|
170
|
+
map.sources = map.sources.map(source =>
|
|
171
|
+
path.join(path.dirname(asset.filePath), source),
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
let sourceMap = null;
|
|
175
|
+
if (map.mappings) {
|
|
176
|
+
sourceMap = new SourceMap(options.projectRoot);
|
|
177
|
+
sourceMap.addVLQMap(map);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
asset.type = 'ts';
|
|
181
|
+
asset.setCode(code);
|
|
182
|
+
asset.setMap(sourceMap);
|
|
183
|
+
return [asset];
|
|
184
|
+
},
|
|
185
|
+
}): Transformer);
|
package/src/collect.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import type {TSModuleGraph} from './TSModuleGraph';
|
|
3
|
+
|
|
4
|
+
import nullthrows from 'nullthrows';
|
|
5
|
+
import ts, {type EntityName} from 'typescript';
|
|
6
|
+
import {TSModule} from './TSModule';
|
|
7
|
+
import {getExportedName, isDeclaration} from './utils';
|
|
8
|
+
|
|
9
|
+
export function collect(
|
|
10
|
+
moduleGraph: TSModuleGraph,
|
|
11
|
+
context: any,
|
|
12
|
+
sourceFile: any,
|
|
13
|
+
): any {
|
|
14
|
+
// Factory only exists on TS >= 4.0
|
|
15
|
+
const {factory = ts} = context;
|
|
16
|
+
|
|
17
|
+
// When module definitions are nested inside each other (e.g with module augmentation),
|
|
18
|
+
// we want to keep track of the hierarchy so we can associated nodes with the right module.
|
|
19
|
+
const moduleStack: Array<?TSModule> = [];
|
|
20
|
+
let _currentModule: ?TSModule;
|
|
21
|
+
let visit = (node: any): any => {
|
|
22
|
+
if (ts.isBundle(node)) {
|
|
23
|
+
return factory.updateBundle(node, ts.visitNodes(node.sourceFiles, visit));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (ts.isModuleDeclaration(node)) {
|
|
27
|
+
moduleStack.push(_currentModule);
|
|
28
|
+
_currentModule = new TSModule();
|
|
29
|
+
moduleGraph.addModule(node.name.text, _currentModule);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!_currentModule) {
|
|
33
|
+
return ts.visitEachChild(node, visit, context);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let currentModule = nullthrows(_currentModule);
|
|
37
|
+
if (ts.isImportDeclaration(node) && node.importClause) {
|
|
38
|
+
if (node.importClause.namedBindings) {
|
|
39
|
+
if (node.importClause.namedBindings.elements) {
|
|
40
|
+
for (let element of node.importClause.namedBindings.elements) {
|
|
41
|
+
currentModule.addImport(
|
|
42
|
+
element.name.text,
|
|
43
|
+
node.moduleSpecifier.text,
|
|
44
|
+
(element.propertyName ?? element.name).text,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
} else if (node.importClause.namedBindings.name) {
|
|
48
|
+
currentModule.addImport(
|
|
49
|
+
node.importClause.namedBindings.name.text,
|
|
50
|
+
node.moduleSpecifier.text,
|
|
51
|
+
'*',
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (node.importClause.name) {
|
|
57
|
+
currentModule.addImport(
|
|
58
|
+
node.importClause.name.text,
|
|
59
|
+
node.moduleSpecifier.text,
|
|
60
|
+
'default',
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (ts.isExportDeclaration(node)) {
|
|
66
|
+
if (node.exportClause) {
|
|
67
|
+
for (let element of node.exportClause.elements) {
|
|
68
|
+
if (node.moduleSpecifier) {
|
|
69
|
+
currentModule.addExport(
|
|
70
|
+
element.name.text,
|
|
71
|
+
(element.propertyName ?? element.name).text,
|
|
72
|
+
node.moduleSpecifier.text,
|
|
73
|
+
);
|
|
74
|
+
} else {
|
|
75
|
+
currentModule.addExport(
|
|
76
|
+
element.name.text,
|
|
77
|
+
(element.propertyName ?? element.name).text,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
currentModule.addWildcardExport(node.moduleSpecifier.text);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
node = ts.visitEachChild(node, visit, context);
|
|
87
|
+
|
|
88
|
+
if (
|
|
89
|
+
ts.isImportTypeNode(node) &&
|
|
90
|
+
ts.isLiteralTypeNode(node.argument) &&
|
|
91
|
+
ts.isStringLiteral(node.argument.literal)
|
|
92
|
+
) {
|
|
93
|
+
let local = `$$atlaspack$import$${moduleGraph.syntheticImportCount++}`;
|
|
94
|
+
let [specifier, entity] = getImportName(node.qualifier, local, factory);
|
|
95
|
+
currentModule.addImport(local, node.argument.literal.text, specifier);
|
|
96
|
+
return factory.createTypeReferenceNode(entity, node.typeArguments);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Handle `export default name;`
|
|
100
|
+
if (ts.isExportAssignment(node) && ts.isIdentifier(node.expression)) {
|
|
101
|
+
currentModule.addExport('default', node.expression.text);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (isDeclaration(node)) {
|
|
105
|
+
if (node.name) {
|
|
106
|
+
currentModule.addLocal(node.name.text, node);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let name = getExportedName(node);
|
|
110
|
+
if (name) {
|
|
111
|
+
currentModule.addLocal(name, node);
|
|
112
|
+
currentModule.addExport(name, name);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (ts.isVariableStatement(node) && node.modifiers) {
|
|
117
|
+
let isExported = node.modifiers.some(
|
|
118
|
+
m => m.kind === ts.SyntaxKind.ExportKeyword,
|
|
119
|
+
);
|
|
120
|
+
for (let v of node.declarationList.declarations) {
|
|
121
|
+
currentModule.addLocal(v.name.text, v);
|
|
122
|
+
if (isExported) {
|
|
123
|
+
currentModule.addExport(v.name.text, v.name.text);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// After we finish traversing the children of a module definition,
|
|
129
|
+
// we need to make sure that subsequent nodes get associated with the next-highest level module.
|
|
130
|
+
if (ts.isModuleDeclaration(node)) {
|
|
131
|
+
_currentModule = moduleStack.pop();
|
|
132
|
+
}
|
|
133
|
+
return node;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
return ts.visitNode(sourceFile, visit);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Traverse down an EntityName to the root identifier. Return that to use as the named import specifier,
|
|
140
|
+
// and collect the remaining parts into a new QualifiedName with the local replacement at the root.
|
|
141
|
+
// import('react').JSX.Element => import {JSX} from 'react'; JSX.Element
|
|
142
|
+
function getImportName(
|
|
143
|
+
qualifier: ?EntityName,
|
|
144
|
+
local: string,
|
|
145
|
+
factory: typeof ts,
|
|
146
|
+
) {
|
|
147
|
+
if (!qualifier) {
|
|
148
|
+
return ['*', factory.createIdentifier(local)];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (qualifier.kind === ts.SyntaxKind.Identifier) {
|
|
152
|
+
return [qualifier.text, factory.createIdentifier(local)];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let [name, entity] = getImportName(qualifier.left, local, factory);
|
|
156
|
+
return [name, factory.createQualifiedName(entity, qualifier.right)];
|
|
157
|
+
}
|