@@ -105,7 +105,7 @@ namespace ts.moduleSpecifiers {
105105 const info = getInfo ( importingSourceFile . path , host ) ;
106106 const modulePaths = getAllModulePaths ( importingSourceFile . path , nodeModulesFileName , host , preferences , options ) ;
107107 return firstDefined ( modulePaths ,
108- modulePath => tryGetModuleNameAsNodeModule ( modulePath , info , importingSourceFile , host , compilerOptions , /*packageNameOnly*/ true , options . overrideImportMode ) ) ;
108+ modulePath => tryGetModuleNameAsNodeModule ( modulePath , info , importingSourceFile , host , compilerOptions , preferences , /*packageNameOnly*/ true , options . overrideImportMode ) ) ;
109109 }
110110
111111 function getModuleSpecifierWorker (
@@ -120,7 +120,7 @@ namespace ts.moduleSpecifiers {
120120 ) : string {
121121 const info = getInfo ( importingSourceFileName , host ) ;
122122 const modulePaths = getAllModulePaths ( importingSourceFileName , toFileName , host , userPreferences , options ) ;
123- return firstDefined ( modulePaths , modulePath => tryGetModuleNameAsNodeModule ( modulePath , info , importingSourceFile , host , compilerOptions , /*packageNameOnly*/ undefined , options . overrideImportMode ) ) ||
123+ return firstDefined ( modulePaths , modulePath => tryGetModuleNameAsNodeModule ( modulePath , info , importingSourceFile , host , compilerOptions , userPreferences , /*packageNameOnly*/ undefined , options . overrideImportMode ) ) ||
124124 getLocalModuleSpecifier ( toFileName , info , compilerOptions , host , preferences ) ;
125125 }
126126
@@ -248,7 +248,7 @@ namespace ts.moduleSpecifiers {
248248 let pathsSpecifiers : string [ ] | undefined ;
249249 let relativeSpecifiers : string [ ] | undefined ;
250250 for ( const modulePath of modulePaths ) {
251- const specifier = tryGetModuleNameAsNodeModule ( modulePath , info , importingSourceFile , host , compilerOptions , /*packageNameOnly*/ undefined , options . overrideImportMode ) ;
251+ const specifier = tryGetModuleNameAsNodeModule ( modulePath , info , importingSourceFile , host , compilerOptions , userPreferences , /*packageNameOnly*/ undefined , options . overrideImportMode ) ;
252252 nodeModulesSpecifiers = append ( nodeModulesSpecifiers , specifier ) ;
253253 if ( specifier && modulePath . isRedirect ) {
254254 // If we got a specifier for a redirect, it was a bare package specifier (e.g. "@foo/bar",
@@ -666,7 +666,7 @@ namespace ts.moduleSpecifiers {
666666 : removeFileExtension ( relativePath ) ;
667667 }
668668
669- function tryGetModuleNameAsNodeModule ( { path, isRedirect } : ModulePath , { getCanonicalFileName, sourceDirectory } : Info , importingSourceFile : SourceFile , host : ModuleSpecifierResolutionHost , options : CompilerOptions , packageNameOnly ?: boolean , overrideMode ?: ModuleKind . ESNext | ModuleKind . CommonJS ) : string | undefined {
669+ function tryGetModuleNameAsNodeModule ( { path, isRedirect } : ModulePath , { getCanonicalFileName, sourceDirectory } : Info , importingSourceFile : SourceFile , host : ModuleSpecifierResolutionHost , options : CompilerOptions , userPreferences : UserPreferences , packageNameOnly ?: boolean , overrideMode ?: ModuleKind . ESNext | ModuleKind . CommonJS ) : string | undefined {
670670 if ( ! host . fileExists || ! host . readFile ) {
671671 return undefined ;
672672 }
@@ -680,8 +680,9 @@ namespace ts.moduleSpecifiers {
680680 let moduleSpecifier = path ;
681681 let isPackageRootPath = false ;
682682 if ( ! packageNameOnly ) {
683+ const preferences = getPreferences ( host , userPreferences , options , importingSourceFile ) ;
683684 let packageRootIndex = parts . packageRootIndex ;
684- let moduleFileNameForExtensionless : string | undefined ;
685+ let moduleFileName : string | undefined ;
685686 while ( true ) {
686687 // If the module could be imported by a directory name, use that directory's name
687688 const { moduleFileToTry, packageRootPath, blockedByExports, verbatimFromExports } = tryDirectoryWithPackageJson ( packageRootIndex ) ;
@@ -698,12 +699,12 @@ namespace ts.moduleSpecifiers {
698699 isPackageRootPath = true ;
699700 break ;
700701 }
701- if ( ! moduleFileNameForExtensionless ) moduleFileNameForExtensionless = moduleFileToTry ;
702+ if ( ! moduleFileName ) moduleFileName = moduleFileToTry ;
702703
703704 // try with next level of directory
704705 packageRootIndex = path . indexOf ( directorySeparator , packageRootIndex + 1 ) ;
705706 if ( packageRootIndex === - 1 ) {
706- moduleSpecifier = getExtensionlessFileName ( moduleFileNameForExtensionless ) ;
707+ moduleSpecifier = removeExtensionAndIndexPostFix ( moduleFileName , preferences . ending , options , host ) ;
707708 break ;
708709 }
709710 }
@@ -768,28 +769,22 @@ namespace ts.moduleSpecifiers {
768769 }
769770 }
770771 // If the file is the main module, it can be imported by the package name
771- const mainFileRelative = packageJsonContent . typings || packageJsonContent . types || packageJsonContent . main ;
772+ const mainFileRelative = packageJsonContent . typings || packageJsonContent . types || packageJsonContent . main || "index.js" ;
772773 if ( isString ( mainFileRelative ) ) {
773774 const mainExportFile = toPath ( mainFileRelative , packageRootPath , getCanonicalFileName ) ;
774775 if ( removeFileExtension ( mainExportFile ) === removeFileExtension ( getCanonicalFileName ( moduleFileToTry ) ) ) {
775776 return { packageRootPath, moduleFileToTry } ;
776777 }
777778 }
778779 }
779- return { moduleFileToTry } ;
780- }
781-
782- function getExtensionlessFileName ( path : string ) : string {
783- // We still have a file name - remove the extension
784- const fullModulePathWithoutExtension = removeFileExtension ( path ) ;
785-
786- // If the file is /index, it can be imported by its directory name
787- // IFF there is not _also_ a file by the same name
788- if ( getCanonicalFileName ( fullModulePathWithoutExtension . substring ( parts . fileNameIndex ) ) === "/index" && ! tryGetAnyFileFromPath ( host , fullModulePathWithoutExtension . substring ( 0 , parts . fileNameIndex ) ) ) {
789- return fullModulePathWithoutExtension . substring ( 0 , parts . fileNameIndex ) ;
780+ else {
781+ // No package.json exists; an index.js will still resolve as the package name
782+ const fileName = getCanonicalFileName ( moduleFileToTry . substring ( parts . packageRootIndex + 1 ) ) ;
783+ if ( fileName === "index.d.ts" || fileName === "index.js" || fileName === "index.ts" || fileName === "index.tsx" ) {
784+ return { moduleFileToTry, packageRootPath } ;
785+ }
790786 }
791-
792- return fullModulePathWithoutExtension ;
787+ return { moduleFileToTry } ;
793788 }
794789 }
795790
@@ -812,13 +807,19 @@ namespace ts.moduleSpecifiers {
812807 } ) ;
813808 }
814809
815- function removeExtensionAndIndexPostFix ( fileName : string , ending : Ending , options : CompilerOptions ) : string {
810+ function removeExtensionAndIndexPostFix ( fileName : string , ending : Ending , options : CompilerOptions , host ?: ModuleSpecifierResolutionHost ) : string {
816811 if ( fileExtensionIsOneOf ( fileName , [ Extension . Json , Extension . Mjs , Extension . Cjs ] ) ) return fileName ;
817812 const noExtension = removeFileExtension ( fileName ) ;
818813 if ( fileExtensionIsOneOf ( fileName , [ Extension . Dmts , Extension . Mts , Extension . Dcts , Extension . Cts ] ) ) return noExtension + getJSExtensionForFile ( fileName , options ) ;
819814 switch ( ending ) {
820815 case Ending . Minimal :
821- return removeSuffix ( noExtension , "/index" ) ;
816+ const withoutIndex = removeSuffix ( noExtension , "/index" ) ;
817+ if ( host && withoutIndex !== noExtension && tryGetAnyFileFromPath ( host , withoutIndex ) ) {
818+ // Can't remove index if there's a file by the same name as the directory.
819+ // Probably more callers should pass `host` so we can determine this?
820+ return noExtension ;
821+ }
822+ return withoutIndex ;
822823 case Ending . Index :
823824 return noExtension ;
824825 case Ending . JsExtension :
0 commit comments