diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index 7b3ab92f60b04..7da8fba65d3a2 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -147,7 +147,9 @@ function getPreferences( return [ModuleSpecifierEnding.JsExtension]; } if (getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Classic) { - return [ModuleSpecifierEnding.Index, ModuleSpecifierEnding.JsExtension]; + return preferredEnding === ModuleSpecifierEnding.JsExtension + ? [ModuleSpecifierEnding.JsExtension, ModuleSpecifierEnding.Index] + : [ModuleSpecifierEnding.Index, ModuleSpecifierEnding.JsExtension]; } switch (preferredEnding) { case ModuleSpecifierEnding.JsExtension: return [ModuleSpecifierEnding.JsExtension, ModuleSpecifierEnding.Minimal, ModuleSpecifierEnding.Index]; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index bf657d5a15dba..74d9e485e3afb 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -9094,6 +9094,8 @@ const allSupportedExtensionsWithJson: readonly Extension[][] = [...allSupportedE export const supportedDeclarationExtensions: readonly Extension[] = [Extension.Dts, Extension.Dcts, Extension.Dmts]; /** @internal */ export const supportedTSImplementationExtensions: readonly Extension[] = [Extension.Ts, Extension.Cts, Extension.Mts, Extension.Tsx]; +/** @internal */ +export const extensionsNotSupportingExtensionlessResolution: readonly Extension[] = [Extension.Mts, Extension.Dmts, Extension.Mjs, Extension.Cts, Extension.Dcts, Extension.Cjs]; /** @internal */ export function getSupportedExtensions(options?: CompilerOptions): readonly Extension[][]; @@ -9156,7 +9158,9 @@ export const enum ModuleSpecifierEnding { /** @internal */ export function usesExtensionsOnImports({ imports }: SourceFile, hasExtension: (text: string) => boolean = or(hasJSFileExtension, hasTSFileExtension)): boolean { - return firstDefined(imports, ({ text }) => pathIsRelative(text) ? hasExtension(text) : undefined) || false; + return firstDefined(imports, ({ text }) => pathIsRelative(text) && !fileExtensionIsOneOf(text, extensionsNotSupportingExtensionlessResolution) + ? hasExtension(text) + : undefined) || false; } /** @internal */ @@ -9197,6 +9201,10 @@ export function getModuleSpecifierEndingPreference(preference: UserPreferences[" emptyArray; for (const specifier of specifiers) { if (pathIsRelative(specifier)) { + if (fileExtensionIsOneOf(specifier, extensionsNotSupportingExtensionlessResolution)) { + // These extensions are not optional, so do not indicate a preference. + continue; + } if (hasTSFileExtension(specifier)) { return ModuleSpecifierEnding.TsExtension; } diff --git a/tests/cases/fourslash/importNameCodeFixInferEndingPreference.ts b/tests/cases/fourslash/importNameCodeFixInferEndingPreference.ts new file mode 100644 index 0000000000000..6047a6a769fcc --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixInferEndingPreference.ts @@ -0,0 +1,21 @@ +/// + +// @module: esnext +// @moduleResolution: bundler + +// @Filename: /a.mts +//// export {}; + +// @Filename: /b.ts +//// export {}; + +// @Filename: /c.ts +//// export const c = 0; + +// @Filename: /main.ts +//// import {} from "./a.mjs"; +//// import {} from "./b"; +//// +//// c/**/; + +verify.importFixModuleSpecifiers("", ["./c"]); diff --git a/tests/cases/fourslash/importNameCodeFixInferEndingPreference_classic.ts b/tests/cases/fourslash/importNameCodeFixInferEndingPreference_classic.ts new file mode 100644 index 0000000000000..08c57e42fd1ea --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFixInferEndingPreference_classic.ts @@ -0,0 +1,19 @@ +/// + +// @module: esnext +// @checkJs: true +// @allowJs: true +// @noEmit: true + +// @Filename: /a.js +//// export const a = 0; + +// @Filename: /b.js +//// export const b = 0; + +// @Filename: /c.js +//// import { a } from "./a.js"; +//// +//// b/**/; + +verify.importFixModuleSpecifiers("", ["./b.js"]);