Skip to content

Commit d7080f7

Browse files
author
Sean Callahan
committed
Fix wildcard exports extensionless resolution in bundler mode
When using moduleResolution: 'bundler' with wildcard exports patterns like "./*": "./src/*", extensionless imports (e.g., import from "pkg/utils") now correctly resolve to TypeScript files (e.g., ./src/utils.ts). The fix adds extension probing specifically in the exports/imports resolution path (loadModuleFromTargetExportOrImport) for extensionless paths in bundler mode, rather than modifying the general loadFileNameFromPackageJsonField function which would cause duplicate lookups in other code paths. Fixes #62909
1 parent b78f089 commit d7080f7

11 files changed

+470
-1
lines changed

src/compiler/moduleNameResolver.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2804,7 +2804,23 @@ function getLoadModuleFromTargetExportOrImport(extensions: Extensions, state: Mo
28042804
const finalPath = toAbsolutePath(pattern ? resolvedTarget.replace(/\*/g, subpath) : resolvedTarget + subpath);
28052805
const inputLink = tryLoadInputFileForPath(finalPath, subpath, combinePaths(scope.packageDirectory, "package.json"), isImports);
28062806
if (inputLink) return inputLink;
2807-
return toSearchResult(withPackageId(scope, loadFileNameFromPackageJsonField(extensions, finalPath, target, /*onlyRecordFailures*/ false, state), state));
2807+
// First try the standard package.json field resolution
2808+
const fromPkgJsonField = loadFileNameFromPackageJsonField(extensions, finalPath, target, /*onlyRecordFailures*/ false, state);
2809+
if (fromPkgJsonField) {
2810+
return toSearchResult(withPackageId(scope, fromPkgJsonField, state));
2811+
}
2812+
// For extensionless paths in bundler mode (non-ESM), try adding extensions (e.g., ./foo -> ./foo.ts)
2813+
// This handles wildcard exports like "./*": "./src/*" where the resolved path has no extension.
2814+
if (!(state.features & NodeResolutionFeatures.EsmMode)) {
2815+
const filename = getBaseFileName(finalPath);
2816+
if (!filename.includes(".")) {
2817+
const fromFile = loadModuleFromFile(extensions, finalPath, /*onlyRecordFailures*/ false, state);
2818+
if (fromFile) {
2819+
return toSearchResult(withPackageId(scope, fromFile, state));
2820+
}
2821+
}
2822+
}
2823+
return toSearchResult(/*value*/ undefined);
28082824
}
28092825
else if (typeof target === "object" && target !== null) { // eslint-disable-line no-restricted-syntax
28102826
if (!Array.isArray(target)) {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//// [tests/cases/compiler/bundlerWildcardExportsExtensionResolution.ts] ////
2+
3+
=== /node_modules/@repo/library/src/utils.ts ===
4+
export function greet(): string {
5+
>greet : Symbol(greet, Decl(utils.ts, 0, 0))
6+
7+
return "hello";
8+
}
9+
10+
=== /node_modules/@repo/library/src/component.tsx ===
11+
export const Component = () => null;
12+
>Component : Symbol(Component, Decl(component.tsx, 0, 12))
13+
14+
=== /node_modules/@repo/library/src/deep/nested/module.ts ===
15+
export const deep = true;
16+
>deep : Symbol(deep, Decl(module.ts, 0, 12))
17+
18+
=== /app.ts ===
19+
// These imports should all resolve successfully in bundler mode
20+
import { greet } from "@repo/library/utils";
21+
>greet : Symbol(greet, Decl(app.ts, 1, 8))
22+
23+
import { Component } from "@repo/library/component";
24+
>Component : Symbol(Component, Decl(app.ts, 2, 8))
25+
26+
import { deep } from "@repo/library/deep/nested/module";
27+
>deep : Symbol(deep, Decl(app.ts, 3, 8))
28+
29+
greet();
30+
>greet : Symbol(greet, Decl(app.ts, 1, 8))
31+
32+
Component;
33+
>Component : Symbol(Component, Decl(app.ts, 2, 8))
34+
35+
deep;
36+
>deep : Symbol(deep, Decl(app.ts, 3, 8))
37+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
[
2+
"File '/node_modules/@repo/library/src/package.json' does not exist.",
3+
"Found 'package.json' at '/node_modules/@repo/library/package.json'.",
4+
"File '/node_modules/@repo/library/src/package.json' does not exist according to earlier cached lookups.",
5+
"File '/node_modules/@repo/library/package.json' exists according to earlier cached lookups.",
6+
"File '/node_modules/@repo/library/src/deep/nested/package.json' does not exist.",
7+
"File '/node_modules/@repo/library/src/deep/package.json' does not exist.",
8+
"File '/node_modules/@repo/library/src/package.json' does not exist according to earlier cached lookups.",
9+
"File '/node_modules/@repo/library/package.json' exists according to earlier cached lookups.",
10+
"======== Resolving module '@repo/library/utils' from '/app.ts'. ========",
11+
"Explicitly specified module resolution kind: 'Bundler'.",
12+
"Resolving in CJS mode with conditions 'import', 'types'.",
13+
"File '/package.json' does not exist.",
14+
"Loading module '@repo/library/utils' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration, JSON.",
15+
"Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.",
16+
"File '/node_modules/@repo/library/package.json' exists according to earlier cached lookups.",
17+
"Using 'exports' subpath './*' with target './src/utils'.",
18+
"File '/node_modules/@repo/library/src/utils.ts' exists - use it as a name resolution result.",
19+
"Resolving real path for '/node_modules/@repo/library/src/utils.ts', result '/node_modules/@repo/library/src/utils.ts'.",
20+
"======== Module name '@repo/library/utils' was successfully resolved to '/node_modules/@repo/library/src/utils.ts'. ========",
21+
"======== Resolving module '@repo/library/component' from '/app.ts'. ========",
22+
"Explicitly specified module resolution kind: 'Bundler'.",
23+
"Resolving in CJS mode with conditions 'import', 'types'.",
24+
"File '/package.json' does not exist according to earlier cached lookups.",
25+
"Loading module '@repo/library/component' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration, JSON.",
26+
"Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.",
27+
"File '/node_modules/@repo/library/package.json' exists according to earlier cached lookups.",
28+
"Using 'exports' subpath './*' with target './src/component'.",
29+
"File '/node_modules/@repo/library/src/component.ts' does not exist.",
30+
"File '/node_modules/@repo/library/src/component.tsx' exists - use it as a name resolution result.",
31+
"Resolving real path for '/node_modules/@repo/library/src/component.tsx', result '/node_modules/@repo/library/src/component.tsx'.",
32+
"======== Module name '@repo/library/component' was successfully resolved to '/node_modules/@repo/library/src/component.tsx'. ========",
33+
"======== Resolving module '@repo/library/deep/nested/module' from '/app.ts'. ========",
34+
"Explicitly specified module resolution kind: 'Bundler'.",
35+
"Resolving in CJS mode with conditions 'import', 'types'.",
36+
"File '/package.json' does not exist according to earlier cached lookups.",
37+
"Loading module '@repo/library/deep/nested/module' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration, JSON.",
38+
"Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.",
39+
"File '/node_modules/@repo/library/package.json' exists according to earlier cached lookups.",
40+
"Using 'exports' subpath './*' with target './src/deep/nested/module'.",
41+
"File '/node_modules/@repo/library/src/deep/nested/module.ts' exists - use it as a name resolution result.",
42+
"Resolving real path for '/node_modules/@repo/library/src/deep/nested/module.ts', result '/node_modules/@repo/library/src/deep/nested/module.ts'.",
43+
"======== Module name '@repo/library/deep/nested/module' was successfully resolved to '/node_modules/@repo/library/src/deep/nested/module.ts'. ========"
44+
]
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//// [tests/cases/compiler/bundlerWildcardExportsExtensionResolution.ts] ////
2+
3+
=== /node_modules/@repo/library/src/utils.ts ===
4+
export function greet(): string {
5+
>greet : () => string
6+
> : ^^^^^^
7+
8+
return "hello";
9+
>"hello" : "hello"
10+
> : ^^^^^^^
11+
}
12+
13+
=== /node_modules/@repo/library/src/component.tsx ===
14+
export const Component = () => null;
15+
>Component : () => any
16+
> : ^^^^^^^^^
17+
>() => null : () => any
18+
> : ^^^^^^^^^
19+
20+
=== /node_modules/@repo/library/src/deep/nested/module.ts ===
21+
export const deep = true;
22+
>deep : true
23+
> : ^^^^
24+
>true : true
25+
> : ^^^^
26+
27+
=== /app.ts ===
28+
// These imports should all resolve successfully in bundler mode
29+
import { greet } from "@repo/library/utils";
30+
>greet : () => string
31+
> : ^^^^^^
32+
33+
import { Component } from "@repo/library/component";
34+
>Component : () => any
35+
> : ^^^^^^^^^
36+
37+
import { deep } from "@repo/library/deep/nested/module";
38+
>deep : true
39+
> : ^^^^
40+
41+
greet();
42+
>greet() : string
43+
> : ^^^^^^
44+
>greet : () => string
45+
> : ^^^^^^
46+
47+
Component;
48+
>Component : () => any
49+
> : ^^^^^^^^^
50+
51+
deep;
52+
>deep : true
53+
> : ^^^^
54+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/app.ts(2,23): error TS2307: Cannot find module '@repo/library/utils' or its corresponding type declarations.
2+
3+
4+
==== /node_modules/@repo/library/package.json (0 errors) ====
5+
{
6+
"name": "@repo/library",
7+
"type": "module",
8+
"exports": {
9+
"./*": "./src/*"
10+
}
11+
}
12+
13+
==== /node_modules/@repo/library/src/utils.ts (0 errors) ====
14+
export function greet(): string {
15+
return "hello";
16+
}
17+
18+
==== /package.json (0 errors) ====
19+
{
20+
"type": "module"
21+
}
22+
23+
==== /app.ts (1 errors) ====
24+
// This import should FAIL in node16 mode - extensionless imports not supported
25+
import { greet } from "@repo/library/utils";
26+
~~~~~~~~~~~~~~~~~~~~~
27+
!!! error TS2307: Cannot find module '@repo/library/utils' or its corresponding type declarations.
28+
29+
greet();
30+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [tests/cases/compiler/node16WildcardExportsRequiresExtension.ts] ////
2+
3+
=== /node_modules/@repo/library/src/utils.ts ===
4+
export function greet(): string {
5+
>greet : Symbol(greet, Decl(utils.ts, 0, 0))
6+
7+
return "hello";
8+
}
9+
10+
=== /app.ts ===
11+
// This import should FAIL in node16 mode - extensionless imports not supported
12+
import { greet } from "@repo/library/utils";
13+
>greet : Symbol(greet, Decl(app.ts, 1, 8))
14+
15+
greet();
16+
>greet : Symbol(greet, Decl(app.ts, 1, 8))
17+

0 commit comments

Comments
 (0)