From d7080f72e841e80d3fa6adb01510c2aaf14cdbad Mon Sep 17 00:00:00 2001 From: Sean Callahan Date: Thu, 18 Dec 2025 20:13:06 -0700 Subject: [PATCH] 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 --- src/compiler/moduleNameResolver.ts | 18 ++- ...WildcardExportsExtensionResolution.symbols | 37 +++++ ...dcardExportsExtensionResolution.trace.json | 44 ++++++ ...erWildcardExportsExtensionResolution.types | 54 +++++++ ...ildcardExportsRequiresExtension.errors.txt | 30 ++++ ...16WildcardExportsRequiresExtension.symbols | 17 ++ ...ildcardExportsRequiresExtension.trace.json | 147 ++++++++++++++++++ ...de16WildcardExportsRequiresExtension.types | 24 +++ ...ndlerWildcardExportsExtensionResolution.ts | 38 +++++ .../node16WildcardExportsRequiresExtension.ts | 33 ++++ ...rdExportsBundlerExtensionlessResolution.ts | 29 ++++ 11 files changed, 470 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/bundlerWildcardExportsExtensionResolution.symbols create mode 100644 tests/baselines/reference/bundlerWildcardExportsExtensionResolution.trace.json create mode 100644 tests/baselines/reference/bundlerWildcardExportsExtensionResolution.types create mode 100644 tests/baselines/reference/node16WildcardExportsRequiresExtension.errors.txt create mode 100644 tests/baselines/reference/node16WildcardExportsRequiresExtension.symbols create mode 100644 tests/baselines/reference/node16WildcardExportsRequiresExtension.trace.json create mode 100644 tests/baselines/reference/node16WildcardExportsRequiresExtension.types create mode 100644 tests/cases/compiler/bundlerWildcardExportsExtensionResolution.ts create mode 100644 tests/cases/compiler/node16WildcardExportsRequiresExtension.ts create mode 100644 tests/cases/fourslash/wildcardExportsBundlerExtensionlessResolution.ts diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 2bf692f6298a6..4af146b89c858 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -2804,7 +2804,23 @@ function getLoadModuleFromTargetExportOrImport(extensions: Extensions, state: Mo const finalPath = toAbsolutePath(pattern ? resolvedTarget.replace(/\*/g, subpath) : resolvedTarget + subpath); const inputLink = tryLoadInputFileForPath(finalPath, subpath, combinePaths(scope.packageDirectory, "package.json"), isImports); if (inputLink) return inputLink; - return toSearchResult(withPackageId(scope, loadFileNameFromPackageJsonField(extensions, finalPath, target, /*onlyRecordFailures*/ false, state), state)); + // First try the standard package.json field resolution + const fromPkgJsonField = loadFileNameFromPackageJsonField(extensions, finalPath, target, /*onlyRecordFailures*/ false, state); + if (fromPkgJsonField) { + return toSearchResult(withPackageId(scope, fromPkgJsonField, state)); + } + // For extensionless paths in bundler mode (non-ESM), try adding extensions (e.g., ./foo -> ./foo.ts) + // This handles wildcard exports like "./*": "./src/*" where the resolved path has no extension. + if (!(state.features & NodeResolutionFeatures.EsmMode)) { + const filename = getBaseFileName(finalPath); + if (!filename.includes(".")) { + const fromFile = loadModuleFromFile(extensions, finalPath, /*onlyRecordFailures*/ false, state); + if (fromFile) { + return toSearchResult(withPackageId(scope, fromFile, state)); + } + } + } + return toSearchResult(/*value*/ undefined); } else if (typeof target === "object" && target !== null) { // eslint-disable-line no-restricted-syntax if (!Array.isArray(target)) { diff --git a/tests/baselines/reference/bundlerWildcardExportsExtensionResolution.symbols b/tests/baselines/reference/bundlerWildcardExportsExtensionResolution.symbols new file mode 100644 index 0000000000000..3e78c5ca209f8 --- /dev/null +++ b/tests/baselines/reference/bundlerWildcardExportsExtensionResolution.symbols @@ -0,0 +1,37 @@ +//// [tests/cases/compiler/bundlerWildcardExportsExtensionResolution.ts] //// + +=== /node_modules/@repo/library/src/utils.ts === +export function greet(): string { +>greet : Symbol(greet, Decl(utils.ts, 0, 0)) + + return "hello"; +} + +=== /node_modules/@repo/library/src/component.tsx === +export const Component = () => null; +>Component : Symbol(Component, Decl(component.tsx, 0, 12)) + +=== /node_modules/@repo/library/src/deep/nested/module.ts === +export const deep = true; +>deep : Symbol(deep, Decl(module.ts, 0, 12)) + +=== /app.ts === +// These imports should all resolve successfully in bundler mode +import { greet } from "@repo/library/utils"; +>greet : Symbol(greet, Decl(app.ts, 1, 8)) + +import { Component } from "@repo/library/component"; +>Component : Symbol(Component, Decl(app.ts, 2, 8)) + +import { deep } from "@repo/library/deep/nested/module"; +>deep : Symbol(deep, Decl(app.ts, 3, 8)) + +greet(); +>greet : Symbol(greet, Decl(app.ts, 1, 8)) + +Component; +>Component : Symbol(Component, Decl(app.ts, 2, 8)) + +deep; +>deep : Symbol(deep, Decl(app.ts, 3, 8)) + diff --git a/tests/baselines/reference/bundlerWildcardExportsExtensionResolution.trace.json b/tests/baselines/reference/bundlerWildcardExportsExtensionResolution.trace.json new file mode 100644 index 0000000000000..22d77648444d0 --- /dev/null +++ b/tests/baselines/reference/bundlerWildcardExportsExtensionResolution.trace.json @@ -0,0 +1,44 @@ +[ + "File '/node_modules/@repo/library/src/package.json' does not exist.", + "Found 'package.json' at '/node_modules/@repo/library/package.json'.", + "File '/node_modules/@repo/library/src/package.json' does not exist according to earlier cached lookups.", + "File '/node_modules/@repo/library/package.json' exists according to earlier cached lookups.", + "File '/node_modules/@repo/library/src/deep/nested/package.json' does not exist.", + "File '/node_modules/@repo/library/src/deep/package.json' does not exist.", + "File '/node_modules/@repo/library/src/package.json' does not exist according to earlier cached lookups.", + "File '/node_modules/@repo/library/package.json' exists according to earlier cached lookups.", + "======== Resolving module '@repo/library/utils' from '/app.ts'. ========", + "Explicitly specified module resolution kind: 'Bundler'.", + "Resolving in CJS mode with conditions 'import', 'types'.", + "File '/package.json' does not exist.", + "Loading module '@repo/library/utils' 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/@repo/library/package.json' exists according to earlier cached lookups.", + "Using 'exports' subpath './*' with target './src/utils'.", + "File '/node_modules/@repo/library/src/utils.ts' exists - use it as a name resolution result.", + "Resolving real path for '/node_modules/@repo/library/src/utils.ts', result '/node_modules/@repo/library/src/utils.ts'.", + "======== Module name '@repo/library/utils' was successfully resolved to '/node_modules/@repo/library/src/utils.ts'. ========", + "======== Resolving module '@repo/library/component' from '/app.ts'. ========", + "Explicitly specified module resolution kind: 'Bundler'.", + "Resolving in CJS mode with conditions 'import', 'types'.", + "File '/package.json' does not exist according to earlier cached lookups.", + "Loading module '@repo/library/component' 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/@repo/library/package.json' exists according to earlier cached lookups.", + "Using 'exports' subpath './*' with target './src/component'.", + "File '/node_modules/@repo/library/src/component.ts' does not exist.", + "File '/node_modules/@repo/library/src/component.tsx' exists - use it as a name resolution result.", + "Resolving real path for '/node_modules/@repo/library/src/component.tsx', result '/node_modules/@repo/library/src/component.tsx'.", + "======== Module name '@repo/library/component' was successfully resolved to '/node_modules/@repo/library/src/component.tsx'. ========", + "======== Resolving module '@repo/library/deep/nested/module' from '/app.ts'. ========", + "Explicitly specified module resolution kind: 'Bundler'.", + "Resolving in CJS mode with conditions 'import', 'types'.", + "File '/package.json' does not exist according to earlier cached lookups.", + "Loading module '@repo/library/deep/nested/module' 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/@repo/library/package.json' exists according to earlier cached lookups.", + "Using 'exports' subpath './*' with target './src/deep/nested/module'.", + "File '/node_modules/@repo/library/src/deep/nested/module.ts' exists - use it as a name resolution result.", + "Resolving real path for '/node_modules/@repo/library/src/deep/nested/module.ts', result '/node_modules/@repo/library/src/deep/nested/module.ts'.", + "======== Module name '@repo/library/deep/nested/module' was successfully resolved to '/node_modules/@repo/library/src/deep/nested/module.ts'. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/bundlerWildcardExportsExtensionResolution.types b/tests/baselines/reference/bundlerWildcardExportsExtensionResolution.types new file mode 100644 index 0000000000000..38e695a102847 --- /dev/null +++ b/tests/baselines/reference/bundlerWildcardExportsExtensionResolution.types @@ -0,0 +1,54 @@ +//// [tests/cases/compiler/bundlerWildcardExportsExtensionResolution.ts] //// + +=== /node_modules/@repo/library/src/utils.ts === +export function greet(): string { +>greet : () => string +> : ^^^^^^ + + return "hello"; +>"hello" : "hello" +> : ^^^^^^^ +} + +=== /node_modules/@repo/library/src/component.tsx === +export const Component = () => null; +>Component : () => any +> : ^^^^^^^^^ +>() => null : () => any +> : ^^^^^^^^^ + +=== /node_modules/@repo/library/src/deep/nested/module.ts === +export const deep = true; +>deep : true +> : ^^^^ +>true : true +> : ^^^^ + +=== /app.ts === +// These imports should all resolve successfully in bundler mode +import { greet } from "@repo/library/utils"; +>greet : () => string +> : ^^^^^^ + +import { Component } from "@repo/library/component"; +>Component : () => any +> : ^^^^^^^^^ + +import { deep } from "@repo/library/deep/nested/module"; +>deep : true +> : ^^^^ + +greet(); +>greet() : string +> : ^^^^^^ +>greet : () => string +> : ^^^^^^ + +Component; +>Component : () => any +> : ^^^^^^^^^ + +deep; +>deep : true +> : ^^^^ + diff --git a/tests/baselines/reference/node16WildcardExportsRequiresExtension.errors.txt b/tests/baselines/reference/node16WildcardExportsRequiresExtension.errors.txt new file mode 100644 index 0000000000000..657ba5f5e76cd --- /dev/null +++ b/tests/baselines/reference/node16WildcardExportsRequiresExtension.errors.txt @@ -0,0 +1,30 @@ +/app.ts(2,23): error TS2307: Cannot find module '@repo/library/utils' or its corresponding type declarations. + + +==== /node_modules/@repo/library/package.json (0 errors) ==== + { + "name": "@repo/library", + "type": "module", + "exports": { + "./*": "./src/*" + } + } + +==== /node_modules/@repo/library/src/utils.ts (0 errors) ==== + export function greet(): string { + return "hello"; + } + +==== /package.json (0 errors) ==== + { + "type": "module" + } + +==== /app.ts (1 errors) ==== + // This import should FAIL in node16 mode - extensionless imports not supported + import { greet } from "@repo/library/utils"; + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2307: Cannot find module '@repo/library/utils' or its corresponding type declarations. + + greet(); + \ No newline at end of file diff --git a/tests/baselines/reference/node16WildcardExportsRequiresExtension.symbols b/tests/baselines/reference/node16WildcardExportsRequiresExtension.symbols new file mode 100644 index 0000000000000..14f0557ea0fd0 --- /dev/null +++ b/tests/baselines/reference/node16WildcardExportsRequiresExtension.symbols @@ -0,0 +1,17 @@ +//// [tests/cases/compiler/node16WildcardExportsRequiresExtension.ts] //// + +=== /node_modules/@repo/library/src/utils.ts === +export function greet(): string { +>greet : Symbol(greet, Decl(utils.ts, 0, 0)) + + return "hello"; +} + +=== /app.ts === +// This import should FAIL in node16 mode - extensionless imports not supported +import { greet } from "@repo/library/utils"; +>greet : Symbol(greet, Decl(app.ts, 1, 8)) + +greet(); +>greet : Symbol(greet, Decl(app.ts, 1, 8)) + diff --git a/tests/baselines/reference/node16WildcardExportsRequiresExtension.trace.json b/tests/baselines/reference/node16WildcardExportsRequiresExtension.trace.json new file mode 100644 index 0000000000000..eebc0d28af0b7 --- /dev/null +++ b/tests/baselines/reference/node16WildcardExportsRequiresExtension.trace.json @@ -0,0 +1,147 @@ +[ + "File '/node_modules/@repo/library/src/package.json' does not exist.", + "Found 'package.json' at '/node_modules/@repo/library/package.json'.", + "Found 'package.json' at '/package.json'.", + "======== Resolving module '@repo/library/utils' from '/app.ts'. ========", + "Explicitly specified module resolution kind: 'Node16'.", + "Resolving in ESM mode with conditions 'import', 'types', 'node'.", + "File '/package.json' exists according to earlier cached lookups.", + "Loading module '@repo/library/utils' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration.", + "Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration.", + "File '/node_modules/@repo/library/package.json' exists according to earlier cached lookups.", + "Using 'exports' subpath './*' with target './src/utils'.", + "Export specifier './utils' does not exist in package.json scope at path '/node_modules/@repo/library'.", + "Directory '/node_modules/@types' does not exist, skipping all lookups in it.", + "Scoped package detected, looking in 'repo__library/utils'", + "Searching all ancestor node_modules directories for fallback extensions: JavaScript.", + "File '/node_modules/@repo/library/package.json' exists according to earlier cached lookups.", + "Using 'exports' subpath './*' with target './src/utils'.", + "Export specifier './utils' does not exist in package.json scope at path '/node_modules/@repo/library'.", + "======== Module name '@repo/library/utils' was not resolved. ========", + "File '/.ts/package.json' does not exist.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups.", + "File '/.ts/package.json' does not exist according to earlier cached lookups.", + "File '/package.json' exists according to earlier cached lookups." +] \ No newline at end of file diff --git a/tests/baselines/reference/node16WildcardExportsRequiresExtension.types b/tests/baselines/reference/node16WildcardExportsRequiresExtension.types new file mode 100644 index 0000000000000..79248777c625c --- /dev/null +++ b/tests/baselines/reference/node16WildcardExportsRequiresExtension.types @@ -0,0 +1,24 @@ +//// [tests/cases/compiler/node16WildcardExportsRequiresExtension.ts] //// + +=== /node_modules/@repo/library/src/utils.ts === +export function greet(): string { +>greet : () => string +> : ^^^^^^ + + return "hello"; +>"hello" : "hello" +> : ^^^^^^^ +} + +=== /app.ts === +// This import should FAIL in node16 mode - extensionless imports not supported +import { greet } from "@repo/library/utils"; +>greet : any +> : ^^^ + +greet(); +>greet() : any +> : ^^^ +>greet : any +> : ^^^ + diff --git a/tests/cases/compiler/bundlerWildcardExportsExtensionResolution.ts b/tests/cases/compiler/bundlerWildcardExportsExtensionResolution.ts new file mode 100644 index 0000000000000..509f29f872d21 --- /dev/null +++ b/tests/cases/compiler/bundlerWildcardExportsExtensionResolution.ts @@ -0,0 +1,38 @@ +// @module: preserve +// @moduleResolution: bundler +// @noEmit: true +// @traceResolution: true +// @jsx: react + +// Test: Wildcard exports should resolve extensionless imports in bundler mode +// Related: https://github.com/microsoft/TypeScript/issues/62909 + +// @filename: /node_modules/@repo/library/package.json +{ + "name": "@repo/library", + "type": "module", + "exports": { + "./*": "./src/*" + } +} + +// @filename: /node_modules/@repo/library/src/utils.ts +export function greet(): string { + return "hello"; +} + +// @filename: /node_modules/@repo/library/src/component.tsx +export const Component = () => null; + +// @filename: /node_modules/@repo/library/src/deep/nested/module.ts +export const deep = true; + +// @filename: /app.ts +// These imports should all resolve successfully in bundler mode +import { greet } from "@repo/library/utils"; +import { Component } from "@repo/library/component"; +import { deep } from "@repo/library/deep/nested/module"; + +greet(); +Component; +deep; diff --git a/tests/cases/compiler/node16WildcardExportsRequiresExtension.ts b/tests/cases/compiler/node16WildcardExportsRequiresExtension.ts new file mode 100644 index 0000000000000..f63a1fcf5ec15 --- /dev/null +++ b/tests/cases/compiler/node16WildcardExportsRequiresExtension.ts @@ -0,0 +1,33 @@ +// @module: node16 +// @moduleResolution: node16 +// @noEmit: true +// @traceResolution: true + +// Test: Wildcard exports should NOT resolve extensionless imports in node16 mode +// This verifies that the fix for bundler mode doesn't affect node16 behavior +// Related: https://github.com/microsoft/TypeScript/issues/62909 + +// @filename: /node_modules/@repo/library/package.json +{ + "name": "@repo/library", + "type": "module", + "exports": { + "./*": "./src/*" + } +} + +// @filename: /node_modules/@repo/library/src/utils.ts +export function greet(): string { + return "hello"; +} + +// @filename: /package.json +{ + "type": "module" +} + +// @filename: /app.ts +// This import should FAIL in node16 mode - extensionless imports not supported +import { greet } from "@repo/library/utils"; + +greet(); diff --git a/tests/cases/fourslash/wildcardExportsBundlerExtensionlessResolution.ts b/tests/cases/fourslash/wildcardExportsBundlerExtensionlessResolution.ts new file mode 100644 index 0000000000000..f02463203a14b --- /dev/null +++ b/tests/cases/fourslash/wildcardExportsBundlerExtensionlessResolution.ts @@ -0,0 +1,29 @@ +/// + +// @module: preserve +// @moduleResolution: bundler +// @jsx: react + +// Test: Verify that extensionless wildcard exports resolve correctly in bundler mode +// and provide proper type information (quickinfo) +// Related: https://github.com/microsoft/TypeScript/issues/62909 + +// @Filename: /node_modules/@repo/library/package.json +//// { +//// "name": "@repo/library", +//// "exports": { +//// "./*": "./src/*" +//// } +//// } + +// @Filename: /node_modules/@repo/library/src/utils.ts +//// export function /*greetDefinition*/greet(): string { +//// return "hello"; +//// } + +// @Filename: /main.ts +//// import { /*greetImport*/greet } from "@repo/library/utils"; +//// /*greetCall*/greet(); + +verify.quickInfoAt("greetImport", "(alias) function greet(): string\nimport greet"); +verify.quickInfoAt("greetCall", "(alias) greet(): string\nimport greet");