Skip to content

Two or more asterisks in subpath specifiers resolve in TS but never resolve at runtime in Node #56343

@wooorm

Description

@wooorm

Demo Repo

https://twitter.com/wooorm/status/1722224965028909254

Which of the following problems are you reporting?

The module specifier resolves at build time, but shouldn't because it doesn't at runtime

Demonstrate the defect described above with a code sample.

The package.json is important:

{
  "name": "double-asterisk",
  "type": "module",
  "exports": {
    "./a/*/b/*/c/*": "./example.js"
  }
}

example.js:

// Fine in TS, fails in Node.
console.log(await import("double-asterisk/a/*/b/*/c/*"));
// Fine in TS, fails in Node.
console.log(await import("double-asterisk/a/*/b/*/c/x"));
// @ts-expect-error: Fails in TS (Cannot find module …), fails in Node.
console.log(await import("double-asterisk/a/*/b/x/c/*"));

(you can replace more asterisks with letters; TS only cares about the last one; Node just never resolves regardless)

Run tsc --showConfig and paste its output here

{
    "compilerOptions": {
        "checkJs": true,
        "lib": [
            "dom",
            "es2022"
        ],
        "module": "node16",
        "strict": true,
        "target": "es2022"
    },
    "files": [
        "./example.js"
    ]
}

Run tsc --traceResolution and paste its output here

Found 'package.json' at '/Users/tilde/Projects/oss/double-asterisk/package.json'.
======== Resolving module 'double-asterisk/a/*/b/*/c/*' from '/Users/tilde/Projects/oss/double-asterisk/example.js'. ========
Module resolution kind is not specified, using 'Node16'.
Resolving in ESM mode with conditions 'import', 'types', 'node'.
File '/Users/tilde/Projects/oss/double-asterisk/package.json' exists according to earlier cached lookups.
Using 'exports' subpath './a/*/b/*/c/*' with target './example.js'.
File name '/Users/tilde/Projects/oss/double-asterisk/example.js' has a '.js' extension - stripping it.
File '/Users/tilde/Projects/oss/double-asterisk/example.ts' does not exist.
File '/Users/tilde/Projects/oss/double-asterisk/example.tsx' does not exist.
File '/Users/tilde/Projects/oss/double-asterisk/example.d.ts' does not exist.
File '/Users/tilde/Projects/oss/double-asterisk/example.js' exists - use it as a name resolution result.
Resolution of non-relative name failed; trying with modern Node resolution features disabled to see if npm library needs configuration update.
File '/Users/tilde/Projects/oss/double-asterisk/package.json' exists according to earlier cached lookups.
Using 'exports' subpath './a/*/b/*/c/*' with target './example.js'.
File name '/Users/tilde/Projects/oss/double-asterisk/example.js' has a '.js' extension - stripping it.
File '/Users/tilde/Projects/oss/double-asterisk/example.ts' does not exist.
File '/Users/tilde/Projects/oss/double-asterisk/example.tsx' does not exist.
File '/Users/tilde/Projects/oss/double-asterisk/example.d.ts' does not exist.
Export specifier './a/*/b/*/c/*' does not exist in package.json scope at path '/Users/tilde/Projects/oss/double-asterisk'.
Loading module 'double-asterisk/a/*/b/*/c/*' from 'node_modules' folder, target file types: TypeScript, Declaration.
Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.
Directory '/Users/tilde/Projects/oss/double-asterisk/node_modules/@types' does not exist, skipping all lookups in it.
Directory '/Users/tilde/Projects/oss/node_modules' does not exist, skipping all lookups in it.
Directory '/Users/tilde/Projects/node_modules' does not exist, skipping all lookups in it.
Directory '/Users/tilde/node_modules' does not exist, skipping all lookups in it.
Directory '/Users/node_modules' does not exist, skipping all lookups in it.
Directory '/node_modules' does not exist, skipping all lookups in it.
Resolving real path for '/Users/tilde/Projects/oss/double-asterisk/example.js', result '/Users/tilde/Projects/oss/double-asterisk/example.js'.
======== Module name 'double-asterisk/a/*/b/*/c/*' was successfully resolved to '/Users/tilde/Projects/oss/double-asterisk/example.js'. ========
======== Resolving module 'double-asterisk/a/*/b/*/c/x' from '/Users/tilde/Projects/oss/double-asterisk/example.js'. ========
Module resolution kind is not specified, using 'Node16'.
Resolving in ESM mode with conditions 'import', 'types', 'node'.
File '/Users/tilde/Projects/oss/double-asterisk/package.json' exists according to earlier cached lookups.
Using 'exports' subpath './a/*/b/*/c/*' with target './example.js'.
File name '/Users/tilde/Projects/oss/double-asterisk/example.js' has a '.js' extension - stripping it.
File '/Users/tilde/Projects/oss/double-asterisk/example.ts' does not exist.
File '/Users/tilde/Projects/oss/double-asterisk/example.tsx' does not exist.
File '/Users/tilde/Projects/oss/double-asterisk/example.d.ts' does not exist.
File '/Users/tilde/Projects/oss/double-asterisk/example.js' exists - use it as a name resolution result.
Resolution of non-relative name failed; trying with modern Node resolution features disabled to see if npm library needs configuration update.
File '/Users/tilde/Projects/oss/double-asterisk/package.json' exists according to earlier cached lookups.
Using 'exports' subpath './a/*/b/*/c/*' with target './example.js'.
File name '/Users/tilde/Projects/oss/double-asterisk/example.js' has a '.js' extension - stripping it.
File '/Users/tilde/Projects/oss/double-asterisk/example.ts' does not exist.
File '/Users/tilde/Projects/oss/double-asterisk/example.tsx' does not exist.
File '/Users/tilde/Projects/oss/double-asterisk/example.d.ts' does not exist.
Export specifier './a/*/b/*/c/x' does not exist in package.json scope at path '/Users/tilde/Projects/oss/double-asterisk'.
Loading module 'double-asterisk/a/*/b/*/c/x' from 'node_modules' folder, target file types: TypeScript, Declaration.
Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.
Directory '/Users/tilde/Projects/oss/double-asterisk/node_modules/@types' does not exist, skipping all lookups in it.
Directory '/Users/tilde/Projects/oss/node_modules' does not exist, skipping all lookups in it.
Directory '/Users/tilde/Projects/node_modules' does not exist, skipping all lookups in it.
Directory '/Users/tilde/node_modules' does not exist, skipping all lookups in it.
Directory '/Users/node_modules' does not exist, skipping all lookups in it.
Directory '/node_modules' does not exist, skipping all lookups in it.
Resolving real path for '/Users/tilde/Projects/oss/double-asterisk/example.js', result '/Users/tilde/Projects/oss/double-asterisk/example.js'.
======== Module name 'double-asterisk/a/*/b/*/c/x' was successfully resolved to '/Users/tilde/Projects/oss/double-asterisk/example.js'. ========
======== Resolving module 'double-asterisk/a/*/b/x/c/*' from '/Users/tilde/Projects/oss/double-asterisk/example.js'. ========
Module resolution kind is not specified, using 'Node16'.
Resolving in ESM mode with conditions 'import', 'types', 'node'.
File '/Users/tilde/Projects/oss/double-asterisk/package.json' exists according to earlier cached lookups.
Export specifier './a/*/b/x/c/*' does not exist in package.json scope at path '/Users/tilde/Projects/oss/double-asterisk'.
Loading module 'double-asterisk/a/*/b/x/c/*' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration.
Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.
Directory '/Users/tilde/Projects/oss/double-asterisk/node_modules/@types' does not exist, skipping all lookups in it.
Directory '/Users/tilde/Projects/oss/node_modules' does not exist, skipping all lookups in it.
Directory '/Users/tilde/Projects/node_modules' does not exist, skipping all lookups in it.
Directory '/Users/tilde/node_modules' does not exist, skipping all lookups in it.
Directory '/Users/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.
Directory '/Users/tilde/Projects/oss/node_modules' does not exist, skipping all lookups in it.
Directory '/Users/tilde/Projects/node_modules' does not exist, skipping all lookups in it.
Directory '/Users/tilde/node_modules' does not exist, skipping all lookups in it.
Directory '/Users/node_modules' does not exist, skipping all lookups in it.
Directory '/node_modules' does not exist, skipping all lookups in it.
======== Module name 'double-asterisk/a/*/b/x/c/*' was not resolved. ========
…

Paste the package.json of the importing module, if it exists

{
  "name": "double-asterisk",
  "type": "module",
  "exports": {
    "./a/*/b/*/c/*": "./example.js"
  }
}

Paste the package.json of the target module, if it exists

same package

Any other comments can go here

Node always fails resolving when subpath specifiers (so the keys in exports) contain two or more asterisks.

See: https://nodejs.org/api/esm.html#resolution-algorithm-specification, PACKAGE_IMPORTS_EXPORTS_RESOLVE, point 2:

Let expansionKeys be the list of keys of matchObj containing only a single "*", sorted by the sorting function PATTERN_KEY_COMPARE which orders in descending order of specificity.

So expansionKeys must be empty for double asterisks.

Also, point 1 cannot match because there are asterisks.

TS should never allow two or more asterisks in specifiers.

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptEffort: ModerateRequires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual".Fix AvailableA PR has been opened for this issueHelp WantedYou can do this

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions