diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index ae2025148fd4b..d2332db89a182 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -24,6 +24,7 @@ import { ExportAssignment, Extension, extensionFromPath, + extensionsNotSupportingExtensionlessResolution, fileExtensionIsOneOf, FileIncludeKind, firstDefined, @@ -91,6 +92,7 @@ import { removeExtension, removeFileExtension, removeSuffix, + removeTrailingDirectorySeparator, ResolutionMode, resolvePath, ScriptKind, @@ -1007,10 +1009,24 @@ function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCan // happens very easily in fourslash tests though, since every test file listed gets included. See // importNameCodeFix_typesVersions.ts for an example.) const mainExportFile = toPath(mainFileRelative, packageRootPath, getCanonicalFileName); - if (removeFileExtension(mainExportFile) === removeFileExtension(getCanonicalFileName(moduleFileToTry))) { + const canonicalModuleFileToTry = getCanonicalFileName(moduleFileToTry); + if (removeFileExtension(mainExportFile) === removeFileExtension(canonicalModuleFileToTry)) { // ^ An arbitrary removal of file extension for this comparison is almost certainly wrong return { packageRootPath, moduleFileToTry }; } + else if ( + packageJsonContent.type !== "module" && + !fileExtensionIsOneOf(canonicalModuleFileToTry, extensionsNotSupportingExtensionlessResolution) && + startsWith(canonicalModuleFileToTry, mainExportFile) && + getDirectoryPath(canonicalModuleFileToTry) === removeTrailingDirectorySeparator(mainExportFile) && + removeFileExtension(getBaseFileName(canonicalModuleFileToTry)) === "index" + ) { + // if mainExportFile is a directory, which contains moduleFileToTry, we just try index file + // example mainExportFile: `pkg/lib` and moduleFileToTry: `pkg/lib/index`, we can use packageRootPath + // but this behavior is deprecated for packages with "type": "module", so we only do this for packages without "type": "module" + // and make sure that the extension on index.{???} is something that supports omitting the extension + return { packageRootPath, moduleFileToTry }; + } } } else { diff --git a/tests/cases/fourslash/autoImportPackageRootPath.ts b/tests/cases/fourslash/autoImportPackageRootPath.ts new file mode 100644 index 0000000000000..843202e89edd9 --- /dev/null +++ b/tests/cases/fourslash/autoImportPackageRootPath.ts @@ -0,0 +1,26 @@ +/// + +// @allowJs: true + +// @Filename: /node_modules/pkg/package.json +//// { +//// "name": "pkg", +//// "version": "1.0.0", +//// "main": "lib", +//// "module": "lib" +//// } + +// @Filename: /node_modules/pkg/lib/index.js +//// export function foo() {}; + +// @Filename: /package.json +//// { +//// "dependencies": { +//// "pkg": "*" +//// } +//// } + +// @Filename: /index.ts +//// foo/**/ + +verify.importFixModuleSpecifiers("", ["pkg"]); diff --git a/tests/cases/fourslash/autoImportPackageRootPathExtension.ts b/tests/cases/fourslash/autoImportPackageRootPathExtension.ts new file mode 100644 index 0000000000000..dc1d08f34bfd9 --- /dev/null +++ b/tests/cases/fourslash/autoImportPackageRootPathExtension.ts @@ -0,0 +1,25 @@ +/// + +// @allowJs: true + +// @Filename: /node_modules/pkg/package.json +//// { +//// "name": "pkg", +//// "version": "1.0.0", +//// "main": "lib" +//// } + +// @Filename: /node_modules/pkg/lib/index.d.mts +//// export declare function foo(): any; + +// @Filename: /package.json +//// { +//// "dependencies": { +//// "pkg": "*" +//// } +//// } + +// @Filename: /index.ts +//// foo/**/ + +verify.importFixModuleSpecifiers("", ["pkg/lib/index.mjs"]); diff --git a/tests/cases/fourslash/autoImportPackageRootPathTypeModule.ts b/tests/cases/fourslash/autoImportPackageRootPathTypeModule.ts new file mode 100644 index 0000000000000..44b0825d5ea99 --- /dev/null +++ b/tests/cases/fourslash/autoImportPackageRootPathTypeModule.ts @@ -0,0 +1,26 @@ +/// + +// @allowJs: true + +// @Filename: /node_modules/pkg/package.json +//// { +//// "name": "pkg", +//// "version": "1.0.0", +//// "main": "lib", +//// "type": "module" +//// } + +// @Filename: /node_modules/pkg/lib/index.js +//// export function foo() {}; + +// @Filename: /package.json +//// { +//// "dependencies": { +//// "pkg": "*" +//// } +//// } + +// @Filename: /index.ts +//// foo/**/ + +verify.importFixModuleSpecifiers("", ["pkg/lib"]);