Skip to content

fix(auto-import): don't suggest module specifiers under shadowed export/import conditions - #64183

Open
erantianantha wants to merge 1 commit into
microsoft:mainfrom
erantianantha:fix/auto-import-shadowed-export-conditions
Open

fix(auto-import): don't suggest module specifiers under shadowed export/import conditions#64183
erantianantha wants to merge 1 commit into
microsoft:mainfrom
erantianantha:fix/auto-import-shadowed-export-conditions

Conversation

@erantianantha

Copy link
Copy Markdown

Fixes #64171

Problem

When generating auto-import suggestions from package.json exports or imports, the compiler walks all matching conditional keys. In runtime resolution (Node.js ESM / nodenext), the resolver commits to the first matching active condition and never falls through to later conditions even if the file target does not exist.

Previously, tryGetModuleNameFromExportsOrImports would continue iterating through subsequent runtime conditions (such as "default"), offering auto-import specifier aliases that fail at runtime with ERR_MODULE_NOT_FOUND.

Solution

  • In tryGetModuleNameFromExportsOrImports, track when an active runtime condition has committed to a valid target using targetCapturesResolution. Once captured, subsequent runtime conditions in that mapping are skipped.
  • Type-only conditions (types and types@...) are kept transparent since they are invisible to runtime resolvers and should neither shadow nor be shadowed by runtime conditions.
  • In loadEntrypointsFromExportMap (resolver.go), properly record preceding runtime conditions in newExcludeConditions so fallback targets are not treated as reachable entrypoints when earlier conditions apply.

Testing

  • Added unit tests in specifiers_test.go verifying targetCapturesResolution across relative paths, bare specifiers, null targets, and nested objects.
  • Added 7 fourslash tests covering exports and # imports with shadowed condition branches.
  • Verified all existing fourslash and module specifier test suites pass.

Copilot AI balanced review requested due to automatic review settings September 6, 2026 21:10
@github-project-automation github-project-automation Bot moved this to Not started in PR Backlog Sep 6, 2026
@typescript-automation typescript-automation Bot added For Backlog Bug PRs that fix a backlog bug labels Sep 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical fallback-reachability logic and invalid-target capture behavior remain incorrect.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Prevents auto-import suggestions for module specifiers shadowed by earlier active package conditions.

Changes:

  • Tracks runtime condition capture during specifier generation.
  • Adds condition exclusions when enumerating package entrypoints.
  • Adds unit and fourslash regression coverage.
File summaries
File Review
tsc/internal/modulespecifiers/specifiers.go Moderate: Invalid string targets must capture resolution because Node throws rather than falling through to later conditions.
tsc/internal/modulespecifiers/specifiers_test.go Tests target-capture behavior; expectations must reflect invalid string targets.
tsc/internal/module/resolver.go Critical: Excluding preceding active conditions unconditionally breaks reachable nested-condition fallbacks; preserve the nested target predicate.
tsc/internal/fourslash/tests/autoImportPackageJsonImportsShadowedCondition4_test.go Tests null imports targets.
tsc/internal/fourslash/tests/autoImportPackageJsonImportsShadowedCondition3_test.go Tests custom conditions.
tsc/internal/fourslash/tests/autoImportPackageJsonImportsShadowedCondition2_test.go Tests active imports targets.
tsc/internal/fourslash/tests/autoImportPackageJsonImportsShadowedCondition1_test.go Tests shadowed imports fallbacks.
tsc/internal/fourslash/tests/autoImportPackageJsonExportsShadowedCondition3_test.go Tests ESM type fallbacks.
tsc/internal/fourslash/tests/autoImportPackageJsonExportsShadowedCondition2_test.go Tests type-condition transparency.
tsc/internal/fourslash/tests/autoImportPackageJsonExportsShadowedCondition1_test.go Tests shadowed exports targets.
Review details

Suppressed comments (3)

tsc/internal/modulespecifiers/specifiers.go:1371

  • Empty arrays also capture the enclosing condition: package-target resolution returns null for an empty array (and for an array whose elements all return undefined), so the conditional-object algorithm does not inspect a later key. core.Some returns false for [], causing { "node": [], "default": "./valid.js" } to incorrectly produce a specifier from default. Arrays should be treated as capturing, and the empty-array test should expect true.
	case packagejson.JSONValueTypeArray:
		return core.Some(target.AsArray(), func(elem packagejson.ExportsOrImports) bool {
			return targetCapturesResolution(elem, conditions, isImports)
		})

tsc/internal/modulespecifiers/specifiers.go:1370

  • The new array capture logic is not applied while traversing arrays: the array branch at lines 1302–1309 still tries every later element until one produces a module name. Consequently, targets such as [null, "./src/*.ts"] (and a valid-but-missing first runtime target) can still produce an alias from the second element even though resolution has already been captured. Stop traversing an array after an element for which targetCapturesResolution is true.
	case packagejson.JSONValueTypeArray:
		return core.Some(target.AsArray(), func(elem packagejson.ExportsOrImports) bool {
			return targetCapturesResolution(elem, conditions, isImports)

tsc/internal/modulespecifiers/specifiers.go:1387

  • This accepts every ./ target, but the resolver rejects targets containing .., ., or node_modules path segments (module/resolver.go:782-793). For an invalid earlier condition such as "./dist/../other.js", this helper therefore marks resolution as captured and suppresses a later fallback that the resolver would actually inspect.
	if strings.HasPrefix(target, "./") {
		return true
	}
  • Files reviewed: 10/10 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 2334 to +2335
for _, prevCondition := range prevConditions {
if excludeConditions == nil {
excludeConditions = &collections.Set[string]{}
}
excludeConditions.Add(prevCondition)
newExcludeConditions.Add(prevCondition)
Comment on lines +1363 to +1365
case packagejson.JSONValueTypeString:
str, _ := target.Value.(string)
return isValidRuntimeTarget(str, isImports)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

For Backlog Bug PRs that fix a backlog bug

Projects

Status: Not started

Development

Successfully merging this pull request may close these issues.

[Auto-import] Quick Fix suggests invalid module specifiers that fail to resolve at runtime

2 participants