Skip to content

Commit b1a85ee

Browse files
committed
Use the typescript transform to remove type imports
should fix sveltejs#153 use first param of emit to only emit for the actual source file
1 parent bdd8bb0 commit b1a85ee

File tree

3 files changed

+120
-2
lines changed

3 files changed

+120
-2
lines changed

src/transformers/typescript.ts

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ function isValidSvelteImportDiagnostic(filename: string, diagnostic: any) {
6767
const importTransformer: ts.TransformerFactory<ts.SourceFile> = context => {
6868
const visit: ts.Visitor = node => {
6969
if (ts.isImportDeclaration(node)) {
70+
if (node.importClause?.isTypeOnly) {
71+
return ts.createEmptyStatement();
72+
}
7073
return ts.createImportDeclaration(
7174
node.decorators,
7275
node.modifiers,
@@ -103,6 +106,72 @@ function isValidSvelteReactiveValueDiagnostic(
103106
return !(usedVar && proposedVar && usedVar === proposedVar);
104107
}
105108

109+
function createImportTransformerFromProgram(program: ts.Program) {
110+
const checker = program.getTypeChecker();
111+
112+
const importedTypeRemoverTransformer: ts.TransformerFactory<ts.SourceFile> = context => {
113+
const visit: ts.Visitor = node => {
114+
if (ts.isImportDeclaration(node)) {
115+
116+
let newImportClause: ts.ImportClause = node.importClause;
117+
118+
if (node.importClause) {
119+
// import type {...} from './blah'
120+
if (node.importClause?.isTypeOnly) {
121+
return ts.createEmptyStatement();
122+
}
123+
124+
// import Blah, { blah } from './blah'
125+
newImportClause = ts.getMutableClone(node.importClause);
126+
127+
// types can't be default exports, so we just worry about { blah } and { blah as name } exports
128+
if (newImportClause.namedBindings && ts.isNamedImports(newImportClause.namedBindings)) {
129+
let newBindings = ts.getMutableClone(newImportClause.namedBindings);
130+
let newElements = [];
131+
132+
for (let spec of newBindings.elements) {
133+
let ident = spec.name;
134+
let symbol = checker.getSymbolAtLocation(ident);
135+
let aliased = checker.getAliasedSymbol(symbol);
136+
if (aliased) {
137+
if ((aliased.flags & (ts.SymbolFlags.TypeAlias | ts.SymbolFlags.Interface)) > 0) {
138+
continue; //We found an imported type, don't add to our new import clause
139+
}
140+
}
141+
newElements.push(spec)
142+
}
143+
144+
if (newElements.length > 0) {
145+
newBindings.elements = ts.createNodeArray(newElements, newBindings.elements.hasTrailingComma);
146+
newImportClause.namedBindings = newBindings;
147+
} else {
148+
newImportClause.namedBindings = undefined;
149+
}
150+
}
151+
152+
//we ended up removing all named bindings and we didn't have a name? nothing left to import.
153+
if (!newImportClause.namedBindings && !newImportClause.name) {
154+
return ts.createEmptyStatement();
155+
}
156+
}
157+
158+
return ts.createImportDeclaration(
159+
node.decorators,
160+
node.modifiers,
161+
newImportClause,
162+
node.moduleSpecifier,
163+
);
164+
}
165+
return ts.visitEachChild(node, child => visit(child), context);
166+
};
167+
168+
return node => ts.visitNode(node, visit);
169+
};
170+
171+
return importedTypeRemoverTransformer;
172+
}
173+
174+
106175
function compileFileFromMemory(
107176
compilerOptions: CompilerOptions,
108177
{ filename, content }: { filename: string; content: string },
@@ -162,12 +231,15 @@ function compileFileFromMemory(
162231
};
163232

164233
const program = ts.createProgram([dummyFileName], compilerOptions, host);
234+
235+
let transformers = { before: [createImportTransformerFromProgram(program)] }
236+
165237
const emitResult = program.emit(
238+
program.getSourceFile(dummyFileName),
166239
undefined,
167240
undefined,
168241
undefined,
169-
undefined,
170-
TS_TRANSFORMERS,
242+
transformers
171243
);
172244

173245
// collect diagnostics without svelte import errors

test/fixtures/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export type AType = "test1" | "test2"
2+
export interface AInterface {
3+
test: string
4+
}
5+
export const AValue: string = "test"
6+
7+
export default "String"

test/transformers/typescript.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,44 @@ describe('transformer - typescript', () => {
162162
expect(diagnostics.some(d => d.code === 2552)).toBe(true);
163163
});
164164

165+
it('should remove imports containing types only', async () => {
166+
const { code } = await transpile(
167+
`
168+
import { AType, AInterface } from './fixtures/types'
169+
let name: AType = "test1";
170+
`,
171+
);
172+
expect(code).not.toContain("/fixtures/types");
173+
});
174+
175+
it('should remove type only imports', async () => {
176+
const { code } = await transpile(
177+
`
178+
import type { AType, AInterface } from './fixtures/types'
179+
let name: AType = "test1";
180+
`,
181+
);
182+
expect(code).not.toContain("/fixtures/types");
183+
});
184+
185+
it('should remove only the types from the imports', async () => {
186+
const { code } = await transpile(
187+
`
188+
import { AValue, AType, AInterface } from './fixtures/types'
189+
let name: AType = "test1";
190+
`,
191+
);
192+
expect(code).toContain("import { AValue } from './fixtures/types'")
193+
});
194+
195+
it('should remove the named imports completely if they were all types', async () => {
196+
const { code } = await transpile(
197+
`
198+
import Default, { AType, AInterface } from './fixtures/types'
199+
let name: AType = "test1";
200+
`,
201+
);
202+
expect(code).toContain("import Default from './fixtures/types'")
203+
});
165204
});
166205
});

0 commit comments

Comments
 (0)