@@ -215,15 +215,15 @@ namespace ts.codefix {
215
215
: getAllReExportingModules ( sourceFile , exportedSymbol , moduleSymbol , symbolName , host , program , /*useAutoImportProvider*/ true ) ;
216
216
const useRequire = shouldUseRequire ( sourceFile , program ) ;
217
217
const preferTypeOnlyImport = compilerOptions . importsNotUsedAsValues === ImportsNotUsedAsValues . Error && ! isSourceFileJS ( sourceFile ) && isValidTypeOnlyAliasUseSite ( getTokenAtPosition ( sourceFile , position ) ) ;
218
- const moduleSpecifier = first ( getNewImportInfos ( program , sourceFile , position , preferTypeOnlyImport , useRequire , exportInfos , host , preferences ) ) . moduleSpecifier ;
218
+ const moduleSpecifier = getBestFix ( getNewImportInfos ( program , sourceFile , position , preferTypeOnlyImport , useRequire , exportInfos , host , preferences ) , sourceFile , program , host ) . moduleSpecifier ;
219
219
const fix = getImportFixForSymbol ( sourceFile , exportInfos , moduleSymbol , symbolName , program , position , preferTypeOnlyImport , useRequire , host , preferences ) ;
220
220
return { moduleSpecifier, codeAction : codeFixActionToCodeAction ( codeActionForFix ( { host, formatContext, preferences } , sourceFile , symbolName , fix , getQuotePreference ( sourceFile , preferences ) ) ) } ;
221
221
}
222
222
223
223
function getImportFixForSymbol ( sourceFile : SourceFile , exportInfos : readonly SymbolExportInfo [ ] , moduleSymbol : Symbol , symbolName : string , program : Program , position : number | undefined , preferTypeOnlyImport : boolean , useRequire : boolean , host : LanguageServiceHost , preferences : UserPreferences ) {
224
224
Debug . assert ( exportInfos . some ( info => info . moduleSymbol === moduleSymbol ) , "Some exportInfo should match the specified moduleSymbol" ) ;
225
225
// We sort the best codefixes first, so taking `first` is best.
226
- return first ( getFixForImport ( exportInfos , symbolName , position , preferTypeOnlyImport , useRequire , program , sourceFile , host , preferences ) ) ;
226
+ return getBestFix ( getFixForImport ( exportInfos , symbolName , position , preferTypeOnlyImport , useRequire , program , sourceFile , host , preferences ) , sourceFile , program , host ) ;
227
227
}
228
228
229
229
function codeFixActionToCodeAction ( { description, changes, commands } : CodeFixAction ) : CodeAction {
@@ -424,28 +424,13 @@ namespace ts.codefix {
424
424
) : readonly ( FixAddNewImport | FixUseImportType ) [ ] {
425
425
const isJs = isSourceFileJS ( sourceFile ) ;
426
426
const compilerOptions = program . getCompilerOptions ( ) ;
427
- const { allowsImportingSpecifier } = createAutoImportFilter ( sourceFile , program , host ) ;
428
-
429
- const choicesForEachExportingModule = flatMap ( moduleSymbols , ( { moduleSymbol, importKind, exportedSymbolIsTypeOnly } ) =>
427
+ return flatMap ( moduleSymbols , ( { moduleSymbol, importKind, exportedSymbolIsTypeOnly } ) =>
430
428
moduleSpecifiers . getModuleSpecifiers ( moduleSymbol , program . getTypeChecker ( ) , compilerOptions , sourceFile , createModuleSpecifierResolutionHost ( program , host ) , preferences )
431
429
. map ( ( moduleSpecifier ) : FixAddNewImport | FixUseImportType =>
432
430
// `position` should only be undefined at a missing jsx namespace, in which case we shouldn't be looking for pure types.
433
431
exportedSymbolIsTypeOnly && isJs
434
432
? { kind : ImportFixKind . ImportType , moduleSpecifier, position : Debug . checkDefined ( position , "position should be defined" ) }
435
433
: { kind : ImportFixKind . AddNew , moduleSpecifier, importKind, useRequire, typeOnly : preferTypeOnlyImport } ) ) ;
436
-
437
- // Sort by presence in package.json, then shortest paths first
438
- return sort ( choicesForEachExportingModule , ( a , b ) => {
439
- const allowsImportingA = allowsImportingSpecifier ( a . moduleSpecifier ) ;
440
- const allowsImportingB = allowsImportingSpecifier ( b . moduleSpecifier ) ;
441
- if ( allowsImportingA && ! allowsImportingB ) {
442
- return - 1 ;
443
- }
444
- if ( allowsImportingB && ! allowsImportingA ) {
445
- return 1 ;
446
- }
447
- return a . moduleSpecifier . length - b . moduleSpecifier . length ;
448
- } ) ;
449
434
}
450
435
451
436
function getFixesForAddImport (
@@ -479,7 +464,31 @@ namespace ts.codefix {
479
464
const info = errorCode === Diagnostics . _0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead . code
480
465
? getFixesInfoForUMDImport ( context , symbolToken )
481
466
: isIdentifier ( symbolToken ) ? getFixesInfoForNonUMDImport ( context , symbolToken , useAutoImportProvider ) : undefined ;
482
- return info && { ...info , fixes : sort ( info . fixes , ( a , b ) => a . kind - b . kind ) } ;
467
+ return info && { ...info , fixes : sortFixes ( info . fixes , context . sourceFile , context . program , context . host ) } ;
468
+ }
469
+
470
+ function sortFixes ( fixes : readonly ImportFix [ ] , sourceFile : SourceFile , program : Program , host : LanguageServiceHost ) : readonly ImportFix [ ] {
471
+ const { allowsImportingSpecifier } = createAutoImportFilter ( sourceFile , program , host ) ;
472
+ return sort ( fixes , ( a , b ) => compareValues ( a . kind , b . kind ) || compareModuleSpecifiers ( a , b , allowsImportingSpecifier ) ) ;
473
+ }
474
+
475
+ function getBestFix < T extends ImportFix > ( fixes : readonly T [ ] , sourceFile : SourceFile , program : Program , host : LanguageServiceHost ) : T {
476
+ // These will always be placed first if available, and are better than other kinds
477
+ if ( fixes [ 0 ] . kind === ImportFixKind . UseNamespace || fixes [ 0 ] . kind === ImportFixKind . AddToExisting ) {
478
+ return fixes [ 0 ] ;
479
+ }
480
+ const { allowsImportingSpecifier } = createAutoImportFilter ( sourceFile , program , host ) ;
481
+ return fixes . reduce ( ( best , fix ) =>
482
+ compareModuleSpecifiers ( fix , best , allowsImportingSpecifier ) === Comparison . LessThan ? fix : best
483
+ ) ;
484
+ }
485
+
486
+ function compareModuleSpecifiers ( a : ImportFix , b : ImportFix , allowsImportingSpecifier : ( specifier : string ) => boolean ) : Comparison {
487
+ if ( a . kind !== ImportFixKind . UseNamespace && b . kind !== ImportFixKind . UseNamespace ) {
488
+ return compareBooleans ( allowsImportingSpecifier ( a . moduleSpecifier ) , allowsImportingSpecifier ( b . moduleSpecifier ) )
489
+ || compareNumberOfDirectorySeparators ( a . moduleSpecifier , b . moduleSpecifier ) ;
490
+ }
491
+ return Comparison . EqualTo ;
483
492
}
484
493
485
494
function getFixesInfoForUMDImport ( { sourceFile, program, host, preferences } : CodeFixContextBase , token : Node ) : FixesInfo | undefined {
0 commit comments