Skip to content

fix(noUnresolvedImports): keep export = namespace members in project scans#9634

Closed
raashish1601 wants to merge 5 commits intobiomejs:mainfrom
raashish1601:contributor-01/biome-9626
Closed

fix(noUnresolvedImports): keep export = namespace members in project scans#9634
raashish1601 wants to merge 5 commits intobiomejs:mainfrom
raashish1601:contributor-01/biome-9626

Conversation

@raashish1601
Copy link
Copy Markdown

Summary

  • preserve namespace members from identifier-based export = declarations during project scans even when type inference is disabled
  • keep the existing type-aware path for export = assignments and add a non-type-inference regression against the React declaration shape
  • add noUnresolvedImports analyzer fixtures proving import { useState } from "react" stays resolved through the project-domain CLI path

Testing

  • cargo test -p biome_module_graph react -- --nocapture
  • cargo test -p biome_js_analyze export_equals --test spec_tests -- --nocapture

Fixes #9626.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 27, 2026

🦋 Changeset detected

Latest commit: 78b8700

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added A-Project Area: project A-Linter Area: linter L-JavaScript Language: JavaScript and super languages labels Mar 27, 2026
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 27, 2026

Merging this PR will not alter performance

✅ 4 untouched benchmarks
⏩ 224 skipped benchmarks1


Comparing raashish1601:contributor-01/biome-9626 (78b8700) with main (c17e08e)2

Open in CodSpeed

Footnotes

  1. 224 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (13b3261) during the generation of this report, so c17e08e was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 27, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7f4a2b1f-b484-4f86-bbad-e8c8972f265f

📥 Commits

Reviewing files that changed from the base of the PR and between 61aa34b and 78b8700.

⛔ Files ignored due to path filters (2)
  • crates/biome_js_analyze/tests/specs/correctness/noUnresolvedImports/export-equals-shared-binding.d.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/correctness/noUnresolvedImports/export-equals-shared.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (2)
  • crates/biome_js_analyze/tests/specs/correctness/noUnresolvedImports/export-equals-shared-binding.d.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnresolvedImports/export-equals-shared.ts
✅ Files skipped from review due to trivial changes (2)
  • crates/biome_js_analyze/tests/specs/correctness/noUnresolvedImports/export-equals-shared.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnresolvedImports/export-equals-shared-binding.d.ts

Walkthrough

The PR updates JS module export collection to surface named exports from export = Namespace patterns. The visitor records an optional local identifier for export = assignments; the collector stores local_name on ExportDefaultAssignment and now finalises exports using the SemanticModel, resolving assigned types and producing member/type-based named exports (preferencing existing named exports for the default). Added .d.ts fixtures and tests (including CLI no-inference cases) and a changeset patch addressing noUnresolvedImports behaviour.

Suggested labels

A-Resolver, A-Diagnostic

Suggested reviewers

  • dyc3
  • mdevils
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(noUnresolvedImports): keep export = namespace members in project scans' accurately and concisely summarises the main change—preserving namespace member resolution for CommonJS-style export assignments during project scans.
Description check ✅ Passed The description clearly relates to the changeset, explaining the preservation of namespace members from identifier-based export assignments, the regression testing approach, and the concrete issue being fixed.
Linked Issues check ✅ Passed The PR directly addresses all coding requirements from issue #9626: it resolves named exports from export = Namespace patterns, adds test coverage for React and shared binding shapes, and ensures the fix applies to both project scans and CLI paths with and without type inference.
Out of Scope Changes check ✅ Passed All changes are tightly scoped to the fix: modifications to export collection logic, type member resolution, test specs covering the export = pattern, and updated type inference tests. No extraneous modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/biome_module_graph/src/js_module_info/collector.rs (1)

908-946: ⚠️ Potential issue | 🟠 Major

The no-inference path still drops members declared on the exported value type.

collect_namespace_exports_for_local_name() only hoists direct bindings from the namespace scope. That fixes the React-style shape, but export = shared declarations whose public API comes from the exported binding’s declared type (declare const shared: { foo(): ... }) still lose foo when infer_types == false, because the own_members() branch above has no populated binding type to inspect in this mode. Please derive members from the binding’s declared type in the thin path as well.

Based on learnings: "Implement module-level (thin) inference to resolve TypeReference::Qualifier variants by looking up declarations in module scopes and handling import statements."

Also applies to: 1019-1047

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_module_graph/tests/spec_tests.rs`:
- Around line 1632-1677: Add a new test mirroring
test_react_named_exports_are_visible_without_type_inference that uses an `@types`
module which uses the "export = shared" pattern (e.g., a declared binding
`declare const shared: { useState: ... }` followed by `export = shared`), call
ModuleGraph::update_graph_for_js_paths(&fs, &project_layout, &added_paths,
false) just like the existing test, and assert that
js_module_info_for_path(...).find_js_exported_symbol(&module_graph, "useState")
returns Some; ensure the test name clearly indicates it's for the export =
shared/no-inference case and reference the same helpers (MemoryFileSystem,
get_added_js_paths, ProjectLayout, update_graph_for_js_paths) so the
no-inference path is exercised for the exported-binding shape.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5252f79c-9617-4813-950c-f261f2ebb091

📥 Commits

Reviewing files that changed from the base of the PR and between e4687fe and 92dc9ea.

⛔ Files ignored due to path filters (2)
  • crates/biome_js_analyze/tests/specs/correctness/noUnresolvedImports/export-equals-namespace.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/correctness/noUnresolvedImports/export-equals-react.d.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (5)
  • crates/biome_js_analyze/tests/specs/correctness/noUnresolvedImports/export-equals-namespace.ts
  • crates/biome_js_analyze/tests/specs/correctness/noUnresolvedImports/export-equals-react.d.ts
  • crates/biome_module_graph/src/js_module_info/collector.rs
  • crates/biome_module_graph/src/js_module_info/visitor.rs
  • crates/biome_module_graph/tests/spec_tests.rs

Comment thread crates/biome_module_graph/tests/spec_tests.rs
@ematipico ematipico added the M-Likely Agent This was likely an automated PR without a human in the loop label Mar 27, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
crates/biome_module_graph/src/js_module_info/collector.rs (1)

961-1003: Consider &self instead of &mut self.

This method doesn't appear to mutate self — it only reads via get_by_resolved_id and resolve_reference, both of which take &self. The mutation is only on finalised_exports.

♻️ Proposed signature change
-    fn collect_member_exports_for_resolved_type(
-        &mut self,
+    fn collect_member_exports_for_resolved_type(
+        &self,
         mut resolved: ResolvedTypeId,
         finalised_exports: &mut IndexMap<Text, JsExport>,
     ) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_module_graph/src/js_module_info/collector.rs` around lines 961 -
1003, The method collect_member_exports_for_resolved_type only reads from the
struct (calling get_by_resolved_id and resolve_reference) and mutates only the
local seen_types and the passed-in finalised_exports, so change its receiver
from &mut self to &self; update the function signature accordingly and ensure
any callers use an immutable borrow (replace &mut collector with &collector
where applicable). Confirm get_by_resolved_id and resolve_reference remain
callable with &self and that no other internal mutations require &mut self;
adjust caller sites to pass mutable finalised_exports as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/biome_module_graph/src/js_module_info/collector.rs`:
- Around line 961-1003: The method collect_member_exports_for_resolved_type only
reads from the struct (calling get_by_resolved_id and resolve_reference) and
mutates only the local seen_types and the passed-in finalised_exports, so change
its receiver from &mut self to &self; update the function signature accordingly
and ensure any callers use an immutable borrow (replace &mut collector with
&collector where applicable). Confirm get_by_resolved_id and resolve_reference
remain callable with &self and that no other internal mutations require &mut
self; adjust caller sites to pass mutable finalised_exports as before.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a42f6d37-d9a1-4961-8200-219733c7b9b6

📥 Commits

Reviewing files that changed from the base of the PR and between 8e248ee and 29b2e3b.

📒 Files selected for processing (2)
  • crates/biome_module_graph/src/js_module_info/collector.rs
  • crates/biome_module_graph/tests/spec_tests.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_module_graph/tests/spec_tests.rs

@ematipico ematipico closed this Mar 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Linter Area: linter A-Project Area: project L-JavaScript Language: JavaScript and super languages M-Likely Agent This was likely an automated PR without a human in the loop

Projects

None yet

Development

Successfully merging this pull request may close these issues.

noUnresolvedImports: cannot resolve named exports from packages using export = in @types definitions (@types/react, @types/lodash)

2 participants