@esportsplus/typescript 0.29.5 → 0.29.6
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/build/compiler/coordinator.js +2 -1
- package/build/compiler/imports.d.ts +1 -0
- package/build/compiler/imports.js +5 -2
- package/package.json +1 -1
- package/pnpm-workspace.yaml +6 -0
- package/src/compiler/coordinator.ts +6 -1
- package/src/compiler/imports.ts +12 -2
- package/tests/compiler/coordinator.test.ts +17 -0
|
@@ -92,7 +92,8 @@ function modify(code, file, pkg, options) {
|
|
|
92
92
|
for (let i = 0, n = found.length; i < n; i++) {
|
|
93
93
|
for (let [name, alias] of found[i].specifiers) {
|
|
94
94
|
if (!remove || (!remove.has(name) && !remove.has(alias))) {
|
|
95
|
-
|
|
95
|
+
let base = name === alias ? name : `${name} as ${alias}`;
|
|
96
|
+
specifiers.add(found[i].typeOnly.has(name) ? `type ${base}` : base);
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
}
|
|
@@ -15,14 +15,17 @@ const all = (file, pkg) => {
|
|
|
15
15
|
if (!ts.isStringLiteral(moduleSpecifier) || moduleSpecifier.text !== pkg) {
|
|
16
16
|
continue;
|
|
17
17
|
}
|
|
18
|
-
let bindings = stmt.importClause?.namedBindings, specifiers = new Map();
|
|
18
|
+
let bindings = stmt.importClause?.namedBindings, declTypeOnly = stmt.importClause?.isTypeOnly ?? false, specifiers = new Map(), typeOnly = new Set();
|
|
19
19
|
if (bindings && ts.isNamedImports(bindings)) {
|
|
20
20
|
for (let j = 0, m = bindings.elements.length; j < m; j++) {
|
|
21
21
|
let element = bindings.elements[j], name = element.name.text, propertyName = element.propertyName?.text || name;
|
|
22
22
|
specifiers.set(propertyName, name);
|
|
23
|
+
if (declTypeOnly || element.isTypeOnly) {
|
|
24
|
+
typeOnly.add(propertyName);
|
|
25
|
+
}
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
|
-
imports.push({ end: stmt.end, specifiers, start: stmt.getStart(file) });
|
|
28
|
+
imports.push({ end: stmt.end, specifiers, start: stmt.getStart(file), typeOnly });
|
|
26
29
|
}
|
|
27
30
|
return imports;
|
|
28
31
|
};
|
package/package.json
CHANGED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Approve dependency build scripts (pnpm errors on unapproved ones during install/publish) and trust
|
|
2
|
+
# our own scope so freshly published first-party deps bypass the minimumReleaseAge gate.
|
|
3
|
+
allowBuilds:
|
|
4
|
+
esbuild: true
|
|
5
|
+
minimumReleaseAgeExclude:
|
|
6
|
+
- '@esportsplus/*'
|
|
@@ -136,7 +136,12 @@ function modify(code: string, file: ts.SourceFile, pkg: string, options: ModifyO
|
|
|
136
136
|
for (let i = 0, n = found.length; i < n; i++) {
|
|
137
137
|
for (let [name, alias] of found[i].specifiers) {
|
|
138
138
|
if (!remove || (!remove.has(name) && !remove.has(alias))) {
|
|
139
|
-
|
|
139
|
+
let base = name === alias ? name : `${name} as ${alias}`;
|
|
140
|
+
|
|
141
|
+
// Preserve type-only specifiers with an inline `type` modifier so the merged import
|
|
142
|
+
// stays erasable — otherwise `import type { X }` re-emits as a runtime `import { X }`
|
|
143
|
+
// and fails at load time when X has no runtime export.
|
|
144
|
+
specifiers.add(found[i].typeOnly.has(name) ? `type ${base}` : base);
|
|
140
145
|
}
|
|
141
146
|
}
|
|
142
147
|
}
|
package/src/compiler/imports.ts
CHANGED
|
@@ -4,6 +4,10 @@ import { ts } from '~/index';
|
|
|
4
4
|
type ImportInfo = {
|
|
5
5
|
end: number;
|
|
6
6
|
specifiers: Map<string, string>;
|
|
7
|
+
// propertyName keys that were imported type-only, whether via a type-only clause
|
|
8
|
+
// (`import type { A }`) or an inline specifier (`import { type A }`). Preserved so a rewrite
|
|
9
|
+
// re-emits them as type imports instead of runtime imports.
|
|
10
|
+
typeOnly: Set<string>;
|
|
7
11
|
start: number;
|
|
8
12
|
};
|
|
9
13
|
|
|
@@ -43,7 +47,9 @@ const all = (file: ts.SourceFile, pkg: string): ImportInfo[] => {
|
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
let bindings = stmt.importClause?.namedBindings,
|
|
46
|
-
|
|
50
|
+
declTypeOnly = stmt.importClause?.isTypeOnly ?? false,
|
|
51
|
+
specifiers = new Map<string, string>(),
|
|
52
|
+
typeOnly = new Set<string>();
|
|
47
53
|
|
|
48
54
|
if (bindings && ts.isNamedImports(bindings)) {
|
|
49
55
|
for (let j = 0, m = bindings.elements.length; j < m; j++) {
|
|
@@ -52,10 +58,14 @@ const all = (file: ts.SourceFile, pkg: string): ImportInfo[] => {
|
|
|
52
58
|
propertyName = element.propertyName?.text || name;
|
|
53
59
|
|
|
54
60
|
specifiers.set(propertyName, name);
|
|
61
|
+
|
|
62
|
+
if (declTypeOnly || element.isTypeOnly) {
|
|
63
|
+
typeOnly.add(propertyName);
|
|
64
|
+
}
|
|
55
65
|
}
|
|
56
66
|
}
|
|
57
67
|
|
|
58
|
-
imports.push({ end: stmt.end, specifiers, start: stmt.getStart(file) });
|
|
68
|
+
imports.push({ end: stmt.end, specifiers, start: stmt.getStart(file), typeOnly });
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
return imports;
|
|
@@ -346,6 +346,23 @@ describe('coordinator.transform', () => {
|
|
|
346
346
|
expect(result.code).not.toMatch(/import\s*\{[^}]*reactive[^}]*\}\s*from\s*'my-pkg'/);
|
|
347
347
|
});
|
|
348
348
|
|
|
349
|
+
it('preserves a type-only import when rewriting a package\'s imports', () => {
|
|
350
|
+
let code = "import { html } from 'my-pkg';\nimport type { Renderable } from 'my-pkg';\nlet x = 1;",
|
|
351
|
+
file = parse(code),
|
|
352
|
+
program = makeProgram(file),
|
|
353
|
+
plugin = makePlugin(() => ({
|
|
354
|
+
imports: [{ namespace: 'NS', package: 'my-pkg', remove: ['html'] }]
|
|
355
|
+
})),
|
|
356
|
+
result = coordinator.transform([plugin], code, file, program, '/root', new Map());
|
|
357
|
+
|
|
358
|
+
expect(result.changed).toBe(true);
|
|
359
|
+
expect(result.code).toContain("import * as NS from 'my-pkg';");
|
|
360
|
+
// Renderable must stay type-only (import type { ... } or inline `type Renderable`) so it is
|
|
361
|
+
// erased at runtime — a runtime `import { Renderable }` fails when it has no runtime export.
|
|
362
|
+
expect(result.code).toMatch(/import\s+type\s*\{[^}]*Renderable|import\s*\{[^}]*type\s+Renderable/);
|
|
363
|
+
expect(result.code).not.toMatch(/import\s*\{\s*Renderable\s*\}\s*from\s*'my-pkg'/);
|
|
364
|
+
});
|
|
365
|
+
|
|
349
366
|
// F-TEST-003: Import manipulation integration tests
|
|
350
367
|
|
|
351
368
|
it('adds specifiers to existing import', () => {
|