@@ -215,15 +215,15 @@ namespace ts.codefix {
215215 : getAllReExportingModules ( sourceFile , exportedSymbol , moduleSymbol , symbolName , host , program , /*useAutoImportProvider*/ true ) ;
216216 const useRequire = shouldUseRequire ( sourceFile , program ) ;
217217 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 ;
219219 const fix = getImportFixForSymbol ( sourceFile , exportInfos , moduleSymbol , symbolName , program , position , preferTypeOnlyImport , useRequire , host , preferences ) ;
220220 return { moduleSpecifier, codeAction : codeFixActionToCodeAction ( codeActionForFix ( { host, formatContext, preferences } , sourceFile , symbolName , fix , getQuotePreference ( sourceFile , preferences ) ) ) } ;
221221 }
222222
223223 function getImportFixForSymbol ( sourceFile : SourceFile , exportInfos : readonly SymbolExportInfo [ ] , moduleSymbol : Symbol , symbolName : string , program : Program , position : number | undefined , preferTypeOnlyImport : boolean , useRequire : boolean , host : LanguageServiceHost , preferences : UserPreferences ) {
224224 Debug . assert ( exportInfos . some ( info => info . moduleSymbol === moduleSymbol ) , "Some exportInfo should match the specified moduleSymbol" ) ;
225225 // 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 ) ;
227227 }
228228
229229 function codeFixActionToCodeAction ( { description, changes, commands } : CodeFixAction ) : CodeAction {
@@ -424,28 +424,13 @@ namespace ts.codefix {
424424 ) : readonly ( FixAddNewImport | FixUseImportType ) [ ] {
425425 const isJs = isSourceFileJS ( sourceFile ) ;
426426 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 } ) =>
430428 moduleSpecifiers . getModuleSpecifiers ( moduleSymbol , program . getTypeChecker ( ) , compilerOptions , sourceFile , createModuleSpecifierResolutionHost ( program , host ) , preferences )
431429 . map ( ( moduleSpecifier ) : FixAddNewImport | FixUseImportType =>
432430 // `position` should only be undefined at a missing jsx namespace, in which case we shouldn't be looking for pure types.
433431 exportedSymbolIsTypeOnly && isJs
434432 ? { kind : ImportFixKind . ImportType , moduleSpecifier, position : Debug . checkDefined ( position , "position should be defined" ) }
435433 : { 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- } ) ;
449434 }
450435
451436 function getFixesForAddImport (
@@ -479,7 +464,31 @@ namespace ts.codefix {
479464 const info = errorCode === Diagnostics . _0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead . code
480465 ? getFixesInfoForUMDImport ( context , symbolToken )
481466 : 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 ;
483492 }
484493
485494 function getFixesInfoForUMDImport ( { sourceFile, program, host, preferences } : CodeFixContextBase , token : Node ) : FixesInfo | undefined {
0 commit comments