diff --git a/internal/modulespecifiers/preferences.go b/internal/modulespecifiers/preferences.go index 16c8e9e4aad..f3981b24166 100644 --- a/internal/modulespecifiers/preferences.go +++ b/internal/modulespecifiers/preferences.go @@ -174,7 +174,13 @@ func GetAllowedEndingsInPreferredOrder( moduleResolution := compilerOptions.GetModuleResolutionKind() moduleResolutionIsNodeNext := core.ModuleResolutionKindNode16 <= moduleResolution && moduleResolution <= core.ModuleResolutionKindNodeNext allowImportingTsExtension := shouldAllowImportingTsExtension(compilerOptions, importingSourceFile.FileName()) - if syntaxImpliedNodeFormat == core.ResolutionModeESM && moduleResolutionIsNodeNext { + // TypeScript uses `(syntaxImpliedNodeFormat ?? impliedNodeFormat)` here - fall back to the + // file's default resolution mode when no syntax-implied mode is given. + effectiveSyntaxMode := syntaxImpliedNodeFormat + if effectiveSyntaxMode == core.ResolutionModeNone { + effectiveSyntaxMode = resolutionMode + } + if effectiveSyntaxMode == core.ResolutionModeESM && moduleResolutionIsNodeNext { if allowImportingTsExtension { return []ModuleSpecifierEnding{ModuleSpecifierEndingTsExtension, ModuleSpecifierEndingJsExtension} } diff --git a/testdata/baselines/reference/compiler/declarationEmitImportSpecifierWithTsExtensionRelativeImport.js b/testdata/baselines/reference/compiler/declarationEmitImportSpecifierWithTsExtensionRelativeImport.js new file mode 100644 index 00000000000..fbdc9677d65 --- /dev/null +++ b/testdata/baselines/reference/compiler/declarationEmitImportSpecifierWithTsExtensionRelativeImport.js @@ -0,0 +1,28 @@ +//// [tests/cases/compiler/declarationEmitImportSpecifierWithTsExtensionRelativeImport.ts] //// + +//// [package.json] +{ "type": "module" } + +//// [package.json] +{ "name": "mylib", "version": "1.0.0", "main": "./lib/index.js" } + +//// [Box.d.ts] +export interface Box { readonly value: T } +export declare function box(value: T): Box; + +//// [helper.ts] +import * as B from 'mylib/lib/Box.js'; +export const inner = B.box('hello'); + +//// [mod.ts] +import { inner } from './helper.ts'; +export const out = inner; + + + + +//// [helper.d.ts] +import * as B from 'mylib/lib/Box.js'; +export declare const inner: B.Box; +//// [mod.d.ts] +export declare const out: import("mylib/lib/Box.js").Box; diff --git a/testdata/baselines/reference/compiler/declarationEmitImportSpecifierWithTsExtensionRelativeImport.symbols b/testdata/baselines/reference/compiler/declarationEmitImportSpecifierWithTsExtensionRelativeImport.symbols new file mode 100644 index 00000000000..d40eb9868ab --- /dev/null +++ b/testdata/baselines/reference/compiler/declarationEmitImportSpecifierWithTsExtensionRelativeImport.symbols @@ -0,0 +1,35 @@ +//// [tests/cases/compiler/declarationEmitImportSpecifierWithTsExtensionRelativeImport.ts] //// + +=== /node_modules/mylib/lib/Box.d.ts === +export interface Box { readonly value: T } +>Box : Symbol(Box, Decl(Box.d.ts, 0, 0)) +>T : Symbol(T, Decl(Box.d.ts, 0, 21)) +>value : Symbol(Box.value, Decl(Box.d.ts, 0, 25)) +>T : Symbol(T, Decl(Box.d.ts, 0, 21)) + +export declare function box(value: T): Box; +>box : Symbol(box, Decl(Box.d.ts, 0, 45)) +>T : Symbol(T, Decl(Box.d.ts, 1, 28)) +>value : Symbol(value, Decl(Box.d.ts, 1, 31)) +>T : Symbol(T, Decl(Box.d.ts, 1, 28)) +>Box : Symbol(Box, Decl(Box.d.ts, 0, 0)) +>T : Symbol(T, Decl(Box.d.ts, 1, 28)) + +=== /helper.ts === +import * as B from 'mylib/lib/Box.js'; +>B : Symbol(B, Decl(helper.ts, 0, 6)) + +export const inner = B.box('hello'); +>inner : Symbol(inner, Decl(helper.ts, 1, 12)) +>B.box : Symbol(B.box, Decl(Box.d.ts, 0, 45)) +>B : Symbol(B, Decl(helper.ts, 0, 6)) +>box : Symbol(B.box, Decl(Box.d.ts, 0, 45)) + +=== /mod.ts === +import { inner } from './helper.ts'; +>inner : Symbol(inner, Decl(mod.ts, 0, 8)) + +export const out = inner; +>out : Symbol(out, Decl(mod.ts, 1, 12)) +>inner : Symbol(inner, Decl(mod.ts, 0, 8)) + diff --git a/testdata/baselines/reference/compiler/declarationEmitImportSpecifierWithTsExtensionRelativeImport.types b/testdata/baselines/reference/compiler/declarationEmitImportSpecifierWithTsExtensionRelativeImport.types new file mode 100644 index 00000000000..67d2b168d20 --- /dev/null +++ b/testdata/baselines/reference/compiler/declarationEmitImportSpecifierWithTsExtensionRelativeImport.types @@ -0,0 +1,30 @@ +//// [tests/cases/compiler/declarationEmitImportSpecifierWithTsExtensionRelativeImport.ts] //// + +=== /node_modules/mylib/lib/Box.d.ts === +export interface Box { readonly value: T } +>value : T + +export declare function box(value: T): Box; +>box : (value: T) => Box +>value : T + +=== /helper.ts === +import * as B from 'mylib/lib/Box.js'; +>B : typeof B + +export const inner = B.box('hello'); +>inner : B.Box +>B.box('hello') : B.Box +>B.box : (value: T) => B.Box +>B : typeof B +>box : (value: T) => B.Box +>'hello' : "hello" + +=== /mod.ts === +import { inner } from './helper.ts'; +>inner : import("mylib/lib/Box.js").Box + +export const out = inner; +>out : import("mylib/lib/Box.js").Box +>inner : import("mylib/lib/Box.js").Box + diff --git a/testdata/tests/cases/compiler/declarationEmitImportSpecifierWithTsExtensionRelativeImport.ts b/testdata/tests/cases/compiler/declarationEmitImportSpecifierWithTsExtensionRelativeImport.ts new file mode 100644 index 00000000000..85934e53472 --- /dev/null +++ b/testdata/tests/cases/compiler/declarationEmitImportSpecifierWithTsExtensionRelativeImport.ts @@ -0,0 +1,26 @@ +// @module: nodenext +// @declaration: true +// @emitDeclarationOnly: true +// @allowImportingTsExtensions: true + +// Regression test for https://github.com/microsoft/typescript-go/issues/4616 +// When a file uses `.ts` extension relative imports with allowImportingTsExtensions, +// declaration emit should preserve `.js` extensions on non-relative (node_modules) imports. + +// @Filename: /package.json +{ "type": "module" } + +// @Filename: /node_modules/mylib/package.json +{ "name": "mylib", "version": "1.0.0", "main": "./lib/index.js" } + +// @Filename: /node_modules/mylib/lib/Box.d.ts +export interface Box { readonly value: T } +export declare function box(value: T): Box; + +// @Filename: /helper.ts +import * as B from 'mylib/lib/Box.js'; +export const inner = B.box('hello'); + +// @Filename: /mod.ts +import { inner } from './helper.ts'; +export const out = inner;