Skip to content

Commit 60d0bb9

Browse files
committed
Don’t touch imports used for module augmentation in non-declaration files since it could change JS emit
1 parent 6faeee4 commit 60d0bb9

File tree

3 files changed

+55
-10
lines changed

3 files changed

+55
-10
lines changed

src/services/organizeImports.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,22 @@ namespace ts.OrganizeImports {
125125
if (name || namedBindings) {
126126
usedImports.push(updateImportDeclarationAndClause(importDecl, name, namedBindings));
127127
}
128-
// If a module is imported to be augmented, keep the import declaration, but without an import clause
128+
// If a module is imported to be augmented, it’s used
129129
else if (hasModuleDeclarationMatchingSpecifier(sourceFile, moduleSpecifier)) {
130-
usedImports.push(createImportDeclaration(
131-
importDecl.decorators,
132-
importDecl.modifiers,
133-
/*importClause*/ undefined,
134-
moduleSpecifier));
130+
// If we’re in a declaration file, it’s safe to remove the import clause from it
131+
if (sourceFile.isDeclarationFile) {
132+
usedImports.push(createImportDeclaration(
133+
importDecl.decorators,
134+
importDecl.modifiers,
135+
/*importClause*/ undefined,
136+
moduleSpecifier));
137+
}
138+
// If we’re not in a declaration file, we can’t remove the import clause even though
139+
// the imported symbols are unused, because removing them makes it look like the import
140+
// declaration has side effects, which will cause it to be preserved in the JS emit.
141+
else {
142+
usedImports.push(importDecl);
143+
}
135144
}
136145
}
137146

@@ -145,10 +154,9 @@ namespace ts.OrganizeImports {
145154

146155
function hasModuleDeclarationMatchingSpecifier(sourceFile: SourceFile, moduleSpecifier: Expression) {
147156
const moduleSpecifierText = isStringLiteral(moduleSpecifier) && moduleSpecifier.text;
148-
return isString(moduleSpecifierText) && some(sourceFile.statements, statement =>
149-
isModuleDeclaration(statement)
150-
&& isStringLiteral(statement.name)
151-
&& statement.name.text === moduleSpecifierText);
157+
return isString(moduleSpecifierText) && some(sourceFile.moduleAugmentations, moduleName =>
158+
isStringLiteral(moduleName)
159+
&& moduleName.text === moduleSpecifierText);
152160
}
153161

154162
function getExternalModuleName(specifier: Expression) {

src/testRunner/unittests/services/organizeImports.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,21 @@ import { } from "lib";
350350
import foo from 'foo';
351351
import { Caseless } from 'caseless';
352352
353+
declare module 'foo' {}
354+
declare module 'caseless' {
355+
interface Caseless {
356+
test(name: KeyType): boolean;
357+
}
358+
}`
359+
});
360+
361+
testOrganizeImports("Unused_preserve_imports_for_module_augmentation_in_non_declaration_file",
362+
{
363+
path: "/test.ts",
364+
content: `
365+
import foo from 'foo';
366+
import { Caseless } from 'caseless';
367+
353368
declare module 'foo' {}
354369
declare module 'caseless' {
355370
interface Caseless {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// ==ORIGINAL==
2+
3+
import foo from 'foo';
4+
import { Caseless } from 'caseless';
5+
6+
declare module 'foo' {}
7+
declare module 'caseless' {
8+
interface Caseless {
9+
test(name: KeyType): boolean;
10+
}
11+
}
12+
// ==ORGANIZED==
13+
14+
import { Caseless } from 'caseless';
15+
import foo from 'foo';
16+
17+
declare module 'foo' {}
18+
declare module 'caseless' {
19+
interface Caseless {
20+
test(name: KeyType): boolean;
21+
}
22+
}

0 commit comments

Comments
 (0)