From 85cddbfb801521cd7527f2239892db15df81cb7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 27 Sep 2026 21:30:12 +0200 Subject: [PATCH 1/2] Add test for ambient module importing itself --- ...ambientModuleImportingItselfNotResolved.js | 27 +++++++++++++++ ...ntModuleImportingItselfNotResolved.symbols | 33 +++++++++++++++++++ ...oduleImportingItselfNotResolved.trace.json | 17 ++++++++++ ...ientModuleImportingItselfNotResolved.types | 33 +++++++++++++++++++ ...ambientModuleImportingItselfNotResolved.ts | 28 ++++++++++++++++ 5 files changed, 138 insertions(+) create mode 100644 tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.js create mode 100644 tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.trace.json create mode 100644 tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.types create mode 100644 tsc/testdata/tests/cases/compiler/ambientModuleImportingItselfNotResolved.ts diff --git a/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.js b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.js new file mode 100644 index 0000000000000..194020a028d0c --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.js @@ -0,0 +1,27 @@ +//// [tests/cases/compiler/ambientModuleImportingItselfNotResolved.ts] //// + +//// [package.json] +{ "name": "foo", "version": "1.0.0", "types": "index.d.ts" } + +//// [index.d.ts] +declare global { + var fooGlobal: string; +} +declare function foo(value: unknown): boolean; +export default foo; + +//// [declarations.d.ts] +declare module "foo" { + import _foo from "foo"; + export function isFoo(value: unknown): boolean; +} + +//// [a.ts] +/// +fooGlobal; + + +//// [a.js] +"use strict"; +/// +fooGlobal; diff --git a/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.symbols b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.symbols new file mode 100644 index 0000000000000..cca90cd897c64 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.symbols @@ -0,0 +1,33 @@ +//// [tests/cases/compiler/ambientModuleImportingItselfNotResolved.ts] //// + +=== /a.ts === +/// +fooGlobal; +>fooGlobal : Symbol(fooGlobal, Decl(index.d.ts, 1, 7)) + +=== /node_modules/foo/index.d.ts === +declare global { +>global : Symbol(global, Decl(index.d.ts, 0, 0)) + + var fooGlobal: string; +>fooGlobal : Symbol(fooGlobal, Decl(index.d.ts, 1, 7)) +} +declare function foo(value: unknown): boolean; +>foo : Symbol(foo, Decl(index.d.ts, 2, 1)) +>value : Symbol(value, Decl(index.d.ts, 3, 21)) + +export default foo; +>foo : Symbol(foo, Decl(index.d.ts, 2, 1)) + +=== /declarations.d.ts === +declare module "foo" { +>"foo" : Symbol(_foo, Decl(declarations.d.ts, 0, 0)) + + import _foo from "foo"; +>_foo : Symbol(_foo, Decl(declarations.d.ts, 1, 10)) + + export function isFoo(value: unknown): boolean; +>isFoo : Symbol(isFoo, Decl(declarations.d.ts, 1, 27)) +>value : Symbol(value, Decl(declarations.d.ts, 2, 26)) +} + diff --git a/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.trace.json b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.trace.json new file mode 100644 index 0000000000000..0739445d9d315 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.trace.json @@ -0,0 +1,17 @@ +======== Resolving module 'foo' from '/declarations.d.ts'. ======== +Explicitly specified module resolution kind: 'Bundler'. +Resolving in CJS mode with conditions 'import', 'types'. +File '/package.json' does not exist. +Loading module 'foo' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration, JSON. +Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration. +Found 'package.json' at '/node_modules/foo/package.json'. +File '/node_modules/foo.ts' does not exist. +File '/node_modules/foo.tsx' does not exist. +File '/node_modules/foo.d.ts' does not exist. +'package.json' does not have a 'typesVersions' field. +'package.json' does not have a 'typings' field. +'package.json' has 'types' field 'index.d.ts' that references '/node_modules/foo/index.d.ts'. +File '/node_modules/foo/index.d.ts' exists - use it as a name resolution result. +'package.json' does not have a 'peerDependencies' field. +Resolving real path for '/node_modules/foo/index.d.ts', result '/node_modules/foo/index.d.ts'. +======== Module name 'foo' was successfully resolved to '/node_modules/foo/index.d.ts' with Package ID 'foo/index.d.ts@1.0.0'. ======== diff --git a/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.types b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.types new file mode 100644 index 0000000000000..654b2d0b5fbd2 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.types @@ -0,0 +1,33 @@ +//// [tests/cases/compiler/ambientModuleImportingItselfNotResolved.ts] //// + +=== /a.ts === +/// +fooGlobal; +>fooGlobal : string + +=== /node_modules/foo/index.d.ts === +declare global { +>global : typeof global + + var fooGlobal: string; +>fooGlobal : string +} +declare function foo(value: unknown): boolean; +>foo : (value: unknown) => boolean +>value : unknown + +export default foo; +>foo : (value: unknown) => boolean + +=== /declarations.d.ts === +declare module "foo" { +>"foo" : typeof _foo + + import _foo from "foo"; +>_foo : typeof _foo + + export function isFoo(value: unknown): boolean; +>isFoo : (value: unknown) => boolean +>value : unknown +} + diff --git a/tsc/testdata/tests/cases/compiler/ambientModuleImportingItselfNotResolved.ts b/tsc/testdata/tests/cases/compiler/ambientModuleImportingItselfNotResolved.ts new file mode 100644 index 0000000000000..1493925999644 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/ambientModuleImportingItselfNotResolved.ts @@ -0,0 +1,28 @@ +// @target: esnext +// @module: preserve +// @moduleResolution: bundler +// @noImplicitReferences: true +// @traceResolution: true + +// An import inside an ambient module declaration that names the same module +// resolves to that declaration, so the real package must not be pulled in. + +// @filename: /node_modules/foo/package.json +{ "name": "foo", "version": "1.0.0", "types": "index.d.ts" } + +// @filename: /node_modules/foo/index.d.ts +declare global { + var fooGlobal: string; +} +declare function foo(value: unknown): boolean; +export default foo; + +// @filename: /declarations.d.ts +declare module "foo" { + import _foo from "foo"; + export function isFoo(value: unknown): boolean; +} + +// @filename: /a.ts +/// +fooGlobal; From 4ac217548b7cdb68fca1541a3e8a9cdb8794ae7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 27 Sep 2026 21:37:30 +0200 Subject: [PATCH 2/2] Resolve imports of a file's own ambient module names to the ambient module --- tsc/internal/compiler/fileloader.go | 12 ++++++++ ...oduleImportingItselfNotResolved.errors.txt | 25 ++++++++++++++++ ...ntModuleImportingItselfNotResolved.symbols | 16 +--------- ...oduleImportingItselfNotResolved.trace.json | 18 +---------- ...ientModuleImportingItselfNotResolved.types | 16 +--------- .../nodeColonModuleResolution.trace.json | 30 ++----------------- 6 files changed, 42 insertions(+), 75 deletions(-) create mode 100644 tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.errors.txt diff --git a/tsc/internal/compiler/fileloader.go b/tsc/internal/compiler/fileloader.go index f8bba578a534a..c37a678d4ffec 100644 --- a/tsc/internal/compiler/fileloader.go +++ b/tsc/internal/compiler/fileloader.go @@ -886,6 +886,18 @@ func (p *fileLoader) resolveImportsAndModuleAugmentations(t *parseTask) { } mode := getModeForUsageLocation(file.FileName(), meta, entry, optionsForFile) + // We know moduleName resolves to an ambient module provided that moduleName: + // - is in the list of ambient modules locally declared in the current source file. + if slices.Contains(file.AmbientModuleNames, moduleName) { + if optionsForFile.TraceResolution.IsTrue() { + resolutionsTrace = append(resolutionsTrace, module.DiagAndArgs{ + Message: diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, + Args: []any{moduleName, file.FileName()}, + }) + } + resolutionsInFile[module.ModeAwareCacheKey{Name: moduleName, Mode: mode}] = &module.ResolvedModule{} + continue + } var resolvedModule *module.ResolvedModule var trace []module.DiagAndArgs var err error diff --git a/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.errors.txt b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.errors.txt new file mode 100644 index 0000000000000..1b2c6e7d3a294 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.errors.txt @@ -0,0 +1,25 @@ +/a.ts(2,1): error TS2304: Cannot find name 'fooGlobal'. + + +==== /a.ts (1 errors) ==== + /// + fooGlobal; + ~~~~~~~~~ +!!! error TS2304: Cannot find name 'fooGlobal'. + +==== /node_modules/foo/package.json (0 errors) ==== + { "name": "foo", "version": "1.0.0", "types": "index.d.ts" } + +==== /node_modules/foo/index.d.ts (0 errors) ==== + declare global { + var fooGlobal: string; + } + declare function foo(value: unknown): boolean; + export default foo; + +==== /declarations.d.ts (0 errors) ==== + declare module "foo" { + import _foo from "foo"; + export function isFoo(value: unknown): boolean; + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.symbols b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.symbols index cca90cd897c64..fab01f6eb827e 100644 --- a/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.symbols +++ b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.symbols @@ -1,23 +1,9 @@ //// [tests/cases/compiler/ambientModuleImportingItselfNotResolved.ts] //// === /a.ts === + /// fooGlobal; ->fooGlobal : Symbol(fooGlobal, Decl(index.d.ts, 1, 7)) - -=== /node_modules/foo/index.d.ts === -declare global { ->global : Symbol(global, Decl(index.d.ts, 0, 0)) - - var fooGlobal: string; ->fooGlobal : Symbol(fooGlobal, Decl(index.d.ts, 1, 7)) -} -declare function foo(value: unknown): boolean; ->foo : Symbol(foo, Decl(index.d.ts, 2, 1)) ->value : Symbol(value, Decl(index.d.ts, 3, 21)) - -export default foo; ->foo : Symbol(foo, Decl(index.d.ts, 2, 1)) === /declarations.d.ts === declare module "foo" { diff --git a/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.trace.json b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.trace.json index 0739445d9d315..d403a0e3b4f6e 100644 --- a/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.trace.json +++ b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.trace.json @@ -1,17 +1 @@ -======== Resolving module 'foo' from '/declarations.d.ts'. ======== -Explicitly specified module resolution kind: 'Bundler'. -Resolving in CJS mode with conditions 'import', 'types'. -File '/package.json' does not exist. -Loading module 'foo' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration, JSON. -Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration. -Found 'package.json' at '/node_modules/foo/package.json'. -File '/node_modules/foo.ts' does not exist. -File '/node_modules/foo.tsx' does not exist. -File '/node_modules/foo.d.ts' does not exist. -'package.json' does not have a 'typesVersions' field. -'package.json' does not have a 'typings' field. -'package.json' has 'types' field 'index.d.ts' that references '/node_modules/foo/index.d.ts'. -File '/node_modules/foo/index.d.ts' exists - use it as a name resolution result. -'package.json' does not have a 'peerDependencies' field. -Resolving real path for '/node_modules/foo/index.d.ts', result '/node_modules/foo/index.d.ts'. -======== Module name 'foo' was successfully resolved to '/node_modules/foo/index.d.ts' with Package ID 'foo/index.d.ts@1.0.0'. ======== +Module 'foo' was resolved as locally declared ambient module in file '/declarations.d.ts'. diff --git a/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.types b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.types index 654b2d0b5fbd2..300793c04bba9 100644 --- a/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.types +++ b/tsc/testdata/baselines/reference/compiler/ambientModuleImportingItselfNotResolved.types @@ -3,21 +3,7 @@ === /a.ts === /// fooGlobal; ->fooGlobal : string - -=== /node_modules/foo/index.d.ts === -declare global { ->global : typeof global - - var fooGlobal: string; ->fooGlobal : string -} -declare function foo(value: unknown): boolean; ->foo : (value: unknown) => boolean ->value : unknown - -export default foo; ->foo : (value: unknown) => boolean +>fooGlobal : any === /declarations.d.ts === declare module "foo" { diff --git a/tsc/testdata/baselines/reference/compiler/nodeColonModuleResolution.trace.json b/tsc/testdata/baselines/reference/compiler/nodeColonModuleResolution.trace.json index 7484c14bb1785..4838932736fcb 100644 --- a/tsc/testdata/baselines/reference/compiler/nodeColonModuleResolution.trace.json +++ b/tsc/testdata/baselines/reference/compiler/nodeColonModuleResolution.trace.json @@ -1,35 +1,9 @@ -======== Resolving module 'ph' from '/a/b/node_modules/@types/node/ph.d.ts'. ======== +Module 'ph' was resolved as locally declared ambient module in file '/a/b/node_modules/@types/node/ph.d.ts'. +======== Resolving module 'node:ph' from '/a/b/main.ts'. ======== Explicitly specified module resolution kind: 'Bundler'. Resolving in CJS mode with conditions 'import', 'types'. -File '/a/b/node_modules/@types/node/package.json' does not exist. -File '/a/b/node_modules/@types/package.json' does not exist. -File '/a/b/node_modules/package.json' does not exist. File '/a/b/package.json' does not exist. File '/a/package.json' does not exist. File '/package.json' does not exist. -Loading module 'ph' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration, JSON. -Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration. -Directory '/a/b/node_modules/@types/node/node_modules' does not exist, skipping all lookups in it. -Directory '/a/b/node_modules/@types/node_modules' does not exist, skipping all lookups in it. -File '/a/b/node_modules/ph.ts' does not exist. -File '/a/b/node_modules/ph.tsx' does not exist. -File '/a/b/node_modules/ph.d.ts' does not exist. -File '/a/b/node_modules/@types/ph.d.ts' does not exist. -Directory '/a/node_modules' does not exist, skipping all lookups in it. -Directory '/node_modules' does not exist, skipping all lookups in it. -Searching all ancestor node_modules directories for fallback extensions: JavaScript, JSON. -Directory '/a/b/node_modules/@types/node/node_modules' does not exist, skipping all lookups in it. -Directory '/a/b/node_modules/@types/node_modules' does not exist, skipping all lookups in it. -File '/a/b/node_modules/ph.js' does not exist. -File '/a/b/node_modules/ph.jsx' does not exist. -Directory '/a/node_modules' does not exist, skipping all lookups in it. -Directory '/node_modules' does not exist, skipping all lookups in it. -======== Module name 'ph' was not resolved. ======== -======== Resolving module 'node:ph' from '/a/b/main.ts'. ======== -Explicitly specified module resolution kind: 'Bundler'. -Resolving in CJS mode with conditions 'import', 'types'. -File '/a/b/package.json' does not exist according to earlier cached lookups. -File '/a/package.json' does not exist according to earlier cached lookups. -File '/package.json' does not exist according to earlier cached lookups. Skipping module 'node:ph' that looks like an absolute URI, target file types: TypeScript, JavaScript, Declaration, JSON. ======== Module name 'node:ph' was not resolved. ========