Skip to content

fix(auto-import): don't suggest # imports that only resolve via condition fallback - #64177

Open
Marwan (marwan562) wants to merge 3 commits into
microsoft:mainfrom
marwan562:fix/64171-autoimport-invalid-specifier
Open

Marwan (marwan562) wants to merge 3 commits into
microsoft:mainfrom
marwan562:fix/64171-autoimport-invalid-specifier

Conversation

@marwan562

Copy link
Copy Markdown

With imports": { "#*": { "node": "./dist/*/index.js", "default": "./dist/*.js" } } and nodenext, auto-import offered both #utils/summarize and #utils/summarize/summarize. The second only resolves in TS by falling back from node to default after the node target misses. Node picks the first matching condition and throws ERR_MODULE_NOT_FOUND instead, so picking that suggestion crashes at runtime.

Reverse mapping in tryGetModuleNameFromExportsOrImports did the same fallback as the resolver. It now mirrors Node first-match: when a runtime-active condition misses, later conditions aren't tried. types/types@ don't block since Node ignores them, nested objects with no active runtime key still fall through, and array fallback still works.

Added cases alongside the existing ones in tsc/internal/modulespecifiers/specifiers_test.go.

Fixes #64171
Related #62439, #50762

Disclosure: I used AI assistance to draft this patch and reviewed and tested it myself.

…tion fallback

Reverse mapping in tryGetModuleNameFromExportsOrImports mirrored TS resolver fallback across conditions, suggesting specifiers like #utils/summarize/summarize that resolve via default only after node misses. Node picks first matching condition and throws on miss, so such suggestions crash at runtime with ERR_MODULE_NOT_FOUND.

Mirror Node first-match semantics: when a runtime-active condition misses, block later conditions. Types-only conditions are ignored at runtime and don't block. Handles nested conditionals without active runtime keys and preserves array fallback.

Closes microsoft#64171
Copilot AI balanced review requested due to automatic review settings September 5, 2026 04:41
@github-project-automation github-project-automation Bot moved this to Not started in PR Backlog Sep 5, 2026
@typescript-automation typescript-automation Bot added the For Backlog Bug PRs that fix a backlog bug label Sep 5, 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

Two moderate resolution-semantics issues could still produce incorrect auto-import behavior.

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

Pull request overview

Updates auto-import reverse mapping to better match Node.js conditional import resolution.

Changes:

  • Stops fallback after matching runtime conditions.
  • Adds conditional, nested, types-only, and array mapping tests.
File summaries
File Review
tsc/internal/modulespecifiers/specifiers.go Requires fixes for deeper conditional objects and Node array-target semantics.
tsc/internal/modulespecifiers/specifiers_test.go Adds regression coverage, but incorrectly expects file-existence fallback for arrays.
Review details
  • Files reviewed: 2/2 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 thread tsc/internal/modulespecifiers/specifiers.go
Comment thread tsc/internal/modulespecifiers/specifiers.go Outdated
@marwan562

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

…rays

Address Copilot review: arrays select first valid string target at runtime (no file-existence fallback), and deeper nested conditionals with inactive keys return undefined and should fallback. Switch reverse mapping to tri-state (matched/blocked/skipped) so terminal misses block later fallback while undefined continues.
@marwan562

Copy link
Copy Markdown
Author

Addressed both Copilot comments:

  • Arrays now treat first valid string as terminal (no file-existence fallback). Second-element match test corrected to expect blocked, first-element still valid. Undefined array entries (inactive conditional) still fall through.
  • Nested conditionals now track terminal vs undefined via tri-state return, so { node: { import: { browser: ... } }, default } with inactive browser correctly falls back to default.

Added deeper-nesting + array-undefined-first cases. All modulespecifiers tests pass.

Copilot review

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

The critical and moderate fallback-handling issues must be resolved before approval.

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

Review details

Suppressed comments (2)

tsc/internal/modulespecifiers/specifiers.go:1337

  • An exhausted array is not always equivalent to an undefined nested conditional. Node treats an empty array as null (terminal), and throws the last invalid-target error when all entries are invalid; only an array whose entries all resolve to undefined may fall through. Returning blocked=false here makes maps such as { "node": [], "default": "./dist/index.js" } suggest the default even though Node rejects that specifier. Track these outcomes separately and propagate a terminal result for empty/all-invalid arrays.
		return "", false

tsc/internal/modulespecifiers/specifiers_test.go:393

  • These cases only invoke the private reverse-mapping helper, but the regression is specifically the LSP auto-import/Quick Fix result. Add a Fourslash regression using the issue's package.json/project layout and assert the exact list with VerifyImportFixModuleSpecifiers; existing package-import auto-import coverage uses that path (for example, tsc/internal/fourslash/tests/autoImportPackageJsonImportsConditions_test.go:10-29). This ensures the invalid suggestion is actually filtered through the full language-server flow.
				result := tryGetModuleNameFromExportsOrImports(
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread tsc/internal/modulespecifiers/specifiers.go Outdated
Comment thread tsc/internal/modulespecifiers/specifiers.go Outdated
…s fallback, handle empty/all-invalid arrays

Address Copilot review on PR 64177:
- String misses now check target validity (same rules as forward
  resolver): invalid targets skipped in arrays, terminal in
  conditionals; valid misses stay terminal.
- Thread inTypesOnly through conditional/array recursion so
  types-only subtrees never block, preserving TS declaration
  fallback that Node ignores at runtime.
- Empty and all-invalid arrays are terminal at runtime; only
  all-undefined arrays fall through.
- Add unit cases and Fourslash regression for issue 64171.
@marwan562

Marwan (marwan562) commented Sep 8, 2026 •

Copy link
Copy Markdown
Author

Addressed the two Sep 8 Copilot threads:

  • String validity (specifiers.go:1322 + 1337): string misses now check isValidPackageTarget (same rules as forward resolver: ./ prefix, bare-specifier allowance for imports, .././node_modules segments). Invalid targets return statusInvalid (skipped in arrays, terminal in conditionals); valid misses stay terminal. ["invalid", "./dist/index.js"] now falls through to the valid entry.
  • Types-only arrays (specifiers.go:1334): threaded inTypesOnly through conditional/array recursion, so "types": ["./missing.d.ts", "./index.d.ts"] preserves TS declaration fallback and never blocks outer default (Node ignores the subtree at runtime).

Also handled suppressed feedback: empty and all-invalid arrays are terminal at runtime (only all-undefined falls through), and added Fourslash regression TestAutoImportPackageJsonImportsInvalidSpecifier using the issue layout with VerifyImportFixModuleSpecifiers.

All modulespecifiers + TestAutoImportPackageJson* Fourslash tests pass.

Copilot review

This branch has not been deployed

No deployments
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