@@ -68,6 +68,7 @@ import {
68
68
Identifier ,
69
69
ImportDeclaration ,
70
70
ImportEqualsDeclaration ,
71
+ importFromModuleSpecifier ,
71
72
insertImports ,
72
73
InterfaceDeclaration ,
73
74
InternalSymbolName ,
@@ -84,6 +85,8 @@ import {
84
85
isImportDeclaration ,
85
86
isImportEqualsDeclaration ,
86
87
isNamedExports ,
88
+ isNamedImports ,
89
+ isObjectBindingPattern ,
87
90
isObjectLiteralExpression ,
88
91
isOmittedExpression ,
89
92
isPrologueDirective ,
@@ -96,6 +99,7 @@ import {
96
99
isStringLiteralLike ,
97
100
isValidTypeOnlyAliasUseSite ,
98
101
isVariableDeclaration ,
102
+ isVariableDeclarationInitializedToRequire ,
99
103
isVariableDeclarationList ,
100
104
isVariableStatement ,
101
105
LanguageServiceHost ,
@@ -194,16 +198,15 @@ function error(notApplicableReason: string) {
194
198
195
199
function doChange ( context : RefactorContext , oldFile : SourceFile , targetFile : string , program : Program , toMove : ToMove , changes : textChanges . ChangeTracker , host : LanguageServiceHost , preferences : UserPreferences ) : void {
196
200
const checker = program . getTypeChecker ( ) ;
197
- const usage = getUsageInfo ( oldFile , toMove . all , checker ) ;
198
201
// For a new file
199
202
if ( ! host . fileExists ( targetFile ) ) {
200
- changes . createNewFile ( oldFile , targetFile , getNewStatementsAndRemoveFromOldFile ( oldFile , targetFile , usage , changes , toMove , program , host , preferences ) ) ;
203
+ changes . createNewFile ( oldFile , targetFile , getNewStatementsAndRemoveFromOldFile ( oldFile , targetFile , getUsageInfo ( oldFile , toMove . all , checker ) , changes , toMove , program , host , preferences ) ) ;
201
204
addNewFileToTsconfig ( program , changes , oldFile . fileName , targetFile , hostGetCanonicalFileName ( host ) ) ;
202
205
}
203
206
else {
204
207
const targetSourceFile = Debug . checkDefined ( program . getSourceFile ( targetFile ) ) ;
205
208
const importAdder = codefix . createImportAdder ( targetSourceFile , context . program , context . preferences , context . host ) ;
206
- getNewStatementsAndRemoveFromOldFile ( oldFile , targetSourceFile , usage , changes , toMove , program , host , preferences , importAdder ) ;
209
+ getNewStatementsAndRemoveFromOldFile ( oldFile , targetSourceFile , getUsageInfo ( oldFile , toMove . all , checker , getExistingImports ( targetSourceFile , checker ) ) , changes , toMove , program , host , preferences , importAdder ) ;
207
210
}
208
211
}
209
212
@@ -993,7 +996,7 @@ function isPureImport(node: Node): boolean {
993
996
}
994
997
995
998
/** @internal */
996
- export function getUsageInfo ( oldFile : SourceFile , toMove : readonly Statement [ ] , checker : TypeChecker ) : UsageInfo {
999
+ export function getUsageInfo ( oldFile : SourceFile , toMove : readonly Statement [ ] , checker : TypeChecker , existingTargetImports : ReadonlySet < Symbol > = new Set ( ) ) : UsageInfo {
997
1000
const movedSymbols = new Set < Symbol > ( ) ;
998
1001
const oldImportsNeededByTargetFile = new Map < Symbol , /*isValidTypeOnlyUseSite*/ boolean > ( ) ;
999
1002
const targetFileImportsFromOldFile = new Set < Symbol > ( ) ;
@@ -1010,9 +1013,17 @@ export function getUsageInfo(oldFile: SourceFile, toMove: readonly Statement[],
1010
1013
movedSymbols . add ( Debug . checkDefined ( isExpressionStatement ( decl ) ? checker . getSymbolAtLocation ( decl . expression . left ) : decl . symbol , "Need a symbol here" ) ) ;
1011
1014
} ) ;
1012
1015
}
1016
+
1017
+ const unusedImportsFromOldFile = new Set < Symbol > ( ) ;
1013
1018
for ( const statement of toMove ) {
1014
1019
forEachReference ( statement , checker , ( symbol , isValidTypeOnlyUseSite ) => {
1015
- if ( ! symbol . declarations ) return ;
1020
+ if ( ! symbol . declarations ) {
1021
+ return ;
1022
+ }
1023
+ if ( existingTargetImports . has ( skipAlias ( symbol , checker ) ) ) {
1024
+ unusedImportsFromOldFile . add ( symbol ) ;
1025
+ return ;
1026
+ }
1016
1027
for ( const decl of symbol . declarations ) {
1017
1028
if ( isInImport ( decl ) ) {
1018
1029
const prevIsTypeOnly = oldImportsNeededByTargetFile . get ( symbol ) ;
@@ -1024,7 +1035,10 @@ export function getUsageInfo(oldFile: SourceFile, toMove: readonly Statement[],
1024
1035
}
1025
1036
} ) ;
1026
1037
}
1027
- const unusedImportsFromOldFile = new Set ( oldImportsNeededByTargetFile . keys ( ) ) ;
1038
+
1039
+ for ( const unusedImport of oldImportsNeededByTargetFile . keys ( ) ) {
1040
+ unusedImportsFromOldFile . add ( unusedImport ) ;
1041
+ }
1028
1042
1029
1043
const oldFileImportsFromTargetFile = new Set < Symbol > ( ) ;
1030
1044
for ( const statement of oldFile . statements ) {
@@ -1228,3 +1242,30 @@ function getOverloadRangeToMove(sourceFile: SourceFile, statement: Statement) {
1228
1242
}
1229
1243
return undefined ;
1230
1244
}
1245
+
1246
+ function getExistingImports ( sourceFile : SourceFile , checker : TypeChecker ) {
1247
+ const imports = new Set < Symbol > ( ) ;
1248
+ for ( const moduleSpecifier of sourceFile . imports ) {
1249
+ const declaration = importFromModuleSpecifier ( moduleSpecifier ) ;
1250
+ if (
1251
+ isImportDeclaration ( declaration ) && declaration . importClause &&
1252
+ declaration . importClause . namedBindings && isNamedImports ( declaration . importClause . namedBindings )
1253
+ ) {
1254
+ for ( const e of declaration . importClause . namedBindings . elements ) {
1255
+ const symbol = checker . getSymbolAtLocation ( e . propertyName || e . name ) ;
1256
+ if ( symbol ) {
1257
+ imports . add ( skipAlias ( symbol , checker ) ) ;
1258
+ }
1259
+ }
1260
+ }
1261
+ if ( isVariableDeclarationInitializedToRequire ( declaration . parent ) && isObjectBindingPattern ( declaration . parent . name ) ) {
1262
+ for ( const e of declaration . parent . name . elements ) {
1263
+ const symbol = checker . getSymbolAtLocation ( e . propertyName || e . name ) ;
1264
+ if ( symbol ) {
1265
+ imports . add ( skipAlias ( symbol , checker ) ) ;
1266
+ }
1267
+ }
1268
+ }
1269
+ }
1270
+ return imports ;
1271
+ }
0 commit comments