-
Notifications
You must be signed in to change notification settings - Fork 13k
Open
Labels
Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScriptAn idea for TypeScript
Description
Demo Repo
https://github.com/kraenhansen/typescript-issue-62439
Which of the following problems are you reporting?
Something else more complicated which I'll explain in more detail
Demonstrate the defect described above with a code sample.
When a package declares a custom condition mapped to null
:
The package shouldn't be loadable when the TS config declares the condition:
{
// ...
"compilerOptions": {
"customConditions": ["my-condition"]
},
}
// consumer.ts
import { bar } from "foo"; // Should fail π₯
Run tsc --showConfig
and paste its output here
{
"compilerOptions": {
"module": "node16",
"moduleResolution": "node16",
"customConditions": [
"my-condition"
],
"target": "es2022",
"moduleDetection": "force",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolvePackageJsonExports": true,
"resolvePackageJsonImports": true,
"useDefineForClassFields": true
},
"files": [
"./consumer.ts"
]
}
Run tsc --traceResolution
and paste its output here
======== Resolving module 'foo' from '/redacted/typescript-issue-62438/consumer.ts'. ========
Explicitly specified module resolution kind: 'Node16'.
Resolving in ESM mode with conditions 'import', 'types', 'node', 'my-condition'.
File '/redacted/typescript-issue-62438/package.json' exists according to earlier cached lookups.
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 '/redacted/typescript-issue-62438/node_modules/foo/package.json'.
Entering conditional exports.
Matched 'exports' condition 'my-condition'.
package.json scope '/redacted/typescript-issue-62438/node_modules/foo' explicitly maps specifier '.' to null.
Failed to resolve under condition 'my-condition'.
Matched 'exports' condition 'default'.
Using 'exports' subpath '.' with target './index.js'.
File name '/redacted/typescript-issue-62438/node_modules/foo/index.js' has a '.js' extension - stripping it.
File '/redacted/typescript-issue-62438/node_modules/foo/index.ts' does not exist.
File '/redacted/typescript-issue-62438/node_modules/foo/index.tsx' does not exist.
File '/redacted/typescript-issue-62438/node_modules/foo/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolved under condition 'default'.
Exiting conditional exports.
Resolving real path for '/redacted/typescript-issue-62438/node_modules/foo/index.d.ts', result '/redacted/typescript-issue-62438/foo/index.d.ts'.
======== Module name 'foo' was successfully resolved to '/redacted/typescript-issue-62438/foo/index.d.ts' with Package ID 'foo/[email protected]'. ========
(I skipped unrelated resolutions)
Paste the package.json
of the importing module, if it exists
{
"name": "ts-custom-condition-bug",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"start": "node consumer.ts",
"start:condition": "node --conditions my-condition consumer.ts",
"tsc": "tsc --noEmit",
"trace": "tsc --noEmit --traceResolution"
},
"devDependencies": {
"typescript": "5.9.2"
},
"dependencies": {
"foo": "./foo"
}
}
Paste the package.json
of the target module, if it exists
{
"name": "foo",
"version": "0.1.0",
"type": "module",
"exports": {
".": {
"my-condition": null,
"default": "./index.js"
}
}
}
Any other comments can go here
If a module declares an export condition with the null
value and the resolution of this module match this export condition, the resolution should fail.
This however is not the behavior of the TypeScript compiler (v5.9.2).
Instead of failing the resolution falls back on the default
condition (see --traceResolution
output below)
======== Resolving module 'foo' from '/redacted/typescript-issue-62438/consumer.ts'. ========
Explicitly specified module resolution kind: 'Node16'.
Resolving in ESM mode with conditions 'import', 'types', 'node', 'my-condition'.
File '/redacted/typescript-issue-62438/package.json' exists according to earlier cached lookups.
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 '/redacted/typescript-issue-62438/node_modules/foo/package.json'.
Entering conditional exports.
Matched 'exports' condition 'my-condition'.
package.json scope '/redacted/typescript-issue-62438/node_modules/foo' explicitly maps specifier '.' to null.
Failed to resolve under condition 'my-condition'.
Matched 'exports' condition 'default'.
Using 'exports' subpath '.' with target './index.js'.
File name '/redacted/typescript-issue-62438/node_modules/foo/index.js' has a '.js' extension - stripping it.
File '/redacted/typescript-issue-62438/node_modules/foo/index.ts' does not exist.
File '/redacted/typescript-issue-62438/node_modules/foo/index.tsx' does not exist.
File '/redacted/typescript-issue-62438/node_modules/foo/index.d.ts' exists - use it as a name resolution result.
'package.json' does not have a 'peerDependencies' field.
Resolved under condition 'default'.
Exiting conditional exports.
Resolving real path for '/redacted/typescript-issue-62438/node_modules/foo/index.d.ts', result '/redacted/typescript-issue-62438/foo/index.d.ts'.
======== Module name 'foo' was successfully resolved to '/redacted/typescript-issue-62438/foo/index.d.ts' with Package ID 'foo/[email protected]'. ========
Notice how this behavior is different from Node.js, when passing the condition (--conditions my-condition
):
> node --conditions my-condition consumer.ts
node:internal/modules/esm/resolve:313
return new ERR_PACKAGE_PATH_NOT_EXPORTED(
^
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in /redacted/typescript-issue-62438/node_modules/foo/package.json imported from /redacted/typescript-issue-62438/consumer.ts
at exportsNotFound (node:internal/modules/esm/resolve:313:10)
at packageExportsResolve (node:internal/modules/esm/resolve:603:13)
at packageResolve (node:internal/modules/esm/resolve:773:12)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at #cachedDefaultResolve (node:internal/modules/esm/loader:717:20)
at ModuleLoader.resolve (node:internal/modules/esm/loader:694:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:308:38)
at ModuleJob._link (node:internal/modules/esm/module_job:183:49) {
code: 'ERR_PACKAGE_PATH_NOT_EXPORTED'
}
Node.js v22.19.0
huntie
Metadata
Metadata
Assignees
Labels
Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScriptAn idea for TypeScript