From 0681544be579636c4e25d5007d54fe8754dd0dba Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 24 Sep 2025 15:57:50 -0700 Subject: [PATCH] Disable conditional exports fallbacks on `null` values --- src/compiler/moduleNameResolver.ts | 6 ++- ...kNull(moduleresolution=bundler).errors.txt | 22 +++++++++++ ...kNull(moduleresolution=bundler).trace.json | 23 ++++++++++++ ...ckNull(moduleresolution=node16).errors.txt | 24 ++++++++++++ ...ckNull(moduleresolution=node16).trace.json | 37 +++++++++++++++++++ ...Null(moduleresolution=nodenext).errors.txt | 24 ++++++++++++ ...Null(moduleresolution=nodenext).trace.json | 37 +++++++++++++++++++ ...onditionalExportsResolutionFallbackNull.ts | 26 +++++++++++++ 8 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=bundler).errors.txt create mode 100644 tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=bundler).trace.json create mode 100644 tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=node16).errors.txt create mode 100644 tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=node16).trace.json create mode 100644 tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=nodenext).errors.txt create mode 100644 tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=nodenext).trace.json create mode 100644 tests/cases/conformance/moduleResolution/conditionalExportsResolutionFallbackNull.ts diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 3fa7da5d5c960..1c5f22e25bd9c 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -2812,7 +2812,9 @@ function getLoadModuleFromTargetExportOrImport(extensions: Extensions, state: Mo const subTarget = (target as MapLike)[condition]; const result = loadModuleFromTargetExportOrImport(subTarget, subpath, pattern, key); if (result) { - traceIfEnabled(state, Diagnostics.Resolved_under_condition_0, condition); + if (result.value) { + traceIfEnabled(state, Diagnostics.Resolved_under_condition_0, condition); + } traceIfEnabled(state, Diagnostics.Exiting_conditional_exports); return result; } @@ -2846,7 +2848,7 @@ function getLoadModuleFromTargetExportOrImport(extensions: Extensions, state: Mo if (state.traceEnabled) { trace(state.host, Diagnostics.package_json_scope_0_explicitly_maps_specifier_1_to_null, scope.packageDirectory, moduleName); } - return toSearchResult(/*value*/ undefined); + return { value: undefined }; } if (state.traceEnabled) { trace(state.host, Diagnostics.package_json_scope_0_has_invalid_type_for_target_of_specifier_1, scope.packageDirectory, moduleName); diff --git a/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=bundler).errors.txt b/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=bundler).errors.txt new file mode 100644 index 0000000000000..5f19ec73e1c3e --- /dev/null +++ b/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=bundler).errors.txt @@ -0,0 +1,22 @@ +/index.mts(1,16): error TS2307: Cannot find module 'dep' or its corresponding type declarations. + + +==== /node_modules/dep/package.json (0 errors) ==== + { + "name": "dep", + "version": "1.0.0", + "exports": { + ".": { + "import": null, + "types": "./dist/index.d.ts" + } + } + } + +==== /node_modules/dep/dist/index.d.ts (0 errors) ==== + export {}; + +==== /index.mts (1 errors) ==== + import {} from "dep"; // Cannot find module 'dep'. + ~~~~~ +!!! error TS2307: Cannot find module 'dep' or its corresponding type declarations. \ No newline at end of file diff --git a/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=bundler).trace.json b/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=bundler).trace.json new file mode 100644 index 0000000000000..f4472d36e07ed --- /dev/null +++ b/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=bundler).trace.json @@ -0,0 +1,23 @@ +[ + "File '/node_modules/dep/dist/package.json' does not exist.", + "Found 'package.json' at '/node_modules/dep/package.json'.", + "======== Resolving module 'dep' from '/index.mts'. ========", + "Explicitly specified module resolution kind: 'Bundler'.", + "Resolving in CJS mode with conditions 'import', 'types'.", + "File '/package.json' does not exist.", + "Loading module 'dep' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration, JSON.", + "Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.", + "File '/node_modules/dep/package.json' exists according to earlier cached lookups.", + "Entering conditional exports.", + "Matched 'exports' condition 'import'.", + "package.json scope '/node_modules/dep' explicitly maps specifier '.' to null.", + "Exiting conditional exports.", + "Directory '/node_modules/@types' does not exist, skipping all lookups in it.", + "Searching all ancestor node_modules directories for fallback extensions: JavaScript, JSON.", + "File '/node_modules/dep/package.json' exists according to earlier cached lookups.", + "Entering conditional exports.", + "Matched 'exports' condition 'import'.", + "package.json scope '/node_modules/dep' explicitly maps specifier '.' to null.", + "Exiting conditional exports.", + "======== Module name 'dep' was not resolved. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=node16).errors.txt b/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=node16).errors.txt new file mode 100644 index 0000000000000..6b925a156c298 --- /dev/null +++ b/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=node16).errors.txt @@ -0,0 +1,24 @@ +error TS5110: Option 'module' must be set to 'Node16' when option 'moduleResolution' is set to 'Node16'. +/index.mts(1,16): error TS2307: Cannot find module 'dep' or its corresponding type declarations. + + +!!! error TS5110: Option 'module' must be set to 'Node16' when option 'moduleResolution' is set to 'Node16'. +==== /node_modules/dep/package.json (0 errors) ==== + { + "name": "dep", + "version": "1.0.0", + "exports": { + ".": { + "import": null, + "types": "./dist/index.d.ts" + } + } + } + +==== /node_modules/dep/dist/index.d.ts (0 errors) ==== + export {}; + +==== /index.mts (1 errors) ==== + import {} from "dep"; // Cannot find module 'dep'. + ~~~~~ +!!! error TS2307: Cannot find module 'dep' or its corresponding type declarations. \ No newline at end of file diff --git a/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=node16).trace.json b/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=node16).trace.json new file mode 100644 index 0000000000000..5728de2d09056 --- /dev/null +++ b/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=node16).trace.json @@ -0,0 +1,37 @@ +[ + "File '/node_modules/dep/dist/package.json' does not exist.", + "Found 'package.json' at '/node_modules/dep/package.json'.", + "======== Resolving module 'dep' from '/index.mts'. ========", + "Explicitly specified module resolution kind: 'Node16'.", + "Resolving in ESM mode with conditions 'import', 'types', 'node'.", + "File '/package.json' does not exist.", + "Loading module 'dep' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration.", + "Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.", + "File '/node_modules/dep/package.json' exists according to earlier cached lookups.", + "Entering conditional exports.", + "Matched 'exports' condition 'import'.", + "package.json scope '/node_modules/dep' explicitly maps specifier '.' to null.", + "Exiting conditional exports.", + "Directory '/node_modules/@types' does not exist, skipping all lookups in it.", + "Searching all ancestor node_modules directories for fallback extensions: JavaScript.", + "File '/node_modules/dep/package.json' exists according to earlier cached lookups.", + "Entering conditional exports.", + "Matched 'exports' condition 'import'.", + "package.json scope '/node_modules/dep' explicitly maps specifier '.' to null.", + "Exiting conditional exports.", + "======== Module name 'dep' was not resolved. ========", + "File '/.ts/package.json' does not exist.", + "File '/package.json' does not exist according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' does not exist according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' does not exist according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' does not exist according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' does not exist according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' does not exist according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' does not exist according to earlier cached lookups." +] \ No newline at end of file diff --git a/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=nodenext).errors.txt b/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=nodenext).errors.txt new file mode 100644 index 0000000000000..b9baf4e18fc68 --- /dev/null +++ b/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=nodenext).errors.txt @@ -0,0 +1,24 @@ +error TS5110: Option 'module' must be set to 'NodeNext' when option 'moduleResolution' is set to 'NodeNext'. +/index.mts(1,16): error TS2307: Cannot find module 'dep' or its corresponding type declarations. + + +!!! error TS5110: Option 'module' must be set to 'NodeNext' when option 'moduleResolution' is set to 'NodeNext'. +==== /node_modules/dep/package.json (0 errors) ==== + { + "name": "dep", + "version": "1.0.0", + "exports": { + ".": { + "import": null, + "types": "./dist/index.d.ts" + } + } + } + +==== /node_modules/dep/dist/index.d.ts (0 errors) ==== + export {}; + +==== /index.mts (1 errors) ==== + import {} from "dep"; // Cannot find module 'dep'. + ~~~~~ +!!! error TS2307: Cannot find module 'dep' or its corresponding type declarations. \ No newline at end of file diff --git a/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=nodenext).trace.json b/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=nodenext).trace.json new file mode 100644 index 0000000000000..eb0820e44f63d --- /dev/null +++ b/tests/baselines/reference/conditionalExportsResolutionFallbackNull(moduleresolution=nodenext).trace.json @@ -0,0 +1,37 @@ +[ + "File '/node_modules/dep/dist/package.json' does not exist.", + "Found 'package.json' at '/node_modules/dep/package.json'.", + "======== Resolving module 'dep' from '/index.mts'. ========", + "Explicitly specified module resolution kind: 'NodeNext'.", + "Resolving in ESM mode with conditions 'import', 'types', 'node'.", + "File '/package.json' does not exist.", + "Loading module 'dep' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration.", + "Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.", + "File '/node_modules/dep/package.json' exists according to earlier cached lookups.", + "Entering conditional exports.", + "Matched 'exports' condition 'import'.", + "package.json scope '/node_modules/dep' explicitly maps specifier '.' to null.", + "Exiting conditional exports.", + "Directory '/node_modules/@types' does not exist, skipping all lookups in it.", + "Searching all ancestor node_modules directories for fallback extensions: JavaScript.", + "File '/node_modules/dep/package.json' exists according to earlier cached lookups.", + "Entering conditional exports.", + "Matched 'exports' condition 'import'.", + "package.json scope '/node_modules/dep' explicitly maps specifier '.' to null.", + "Exiting conditional exports.", + "======== Module name 'dep' was not resolved. ========", + "File '/.ts/package.json' does not exist.", + "File '/package.json' does not exist according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' does not exist according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' does not exist according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' does not exist according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' does not exist according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' does not exist according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' does not exist according to earlier cached lookups." +] \ No newline at end of file diff --git a/tests/cases/conformance/moduleResolution/conditionalExportsResolutionFallbackNull.ts b/tests/cases/conformance/moduleResolution/conditionalExportsResolutionFallbackNull.ts new file mode 100644 index 0000000000000..f7029468f8236 --- /dev/null +++ b/tests/cases/conformance/moduleResolution/conditionalExportsResolutionFallbackNull.ts @@ -0,0 +1,26 @@ +// @module: esnext +// @moduleResolution: node16,nodenext,bundler +// @traceResolution: true +// @allowJs: true +// @noTypesAndSymbols: true +// @noEmit: true + +// We fixed https://github.com/microsoft/TypeScript/issues/50762 for `null` only + +// @Filename: /node_modules/dep/package.json +{ + "name": "dep", + "version": "1.0.0", + "exports": { + ".": { + "import": null, + "types": "./dist/index.d.ts" + } + } +} + +// @Filename: /node_modules/dep/dist/index.d.ts +export {}; + +// @Filename: /index.mts +import {} from "dep"; // Cannot find module 'dep'. \ No newline at end of file