Skip to content

feat(rsdoctor): expose export usage graph#14291

Open
JSerFeng wants to merge 1 commit into
mainfrom
fy/rsdoctor-export-usage-graph
Open

feat(rsdoctor): expose export usage graph#14291
JSerFeng wants to merge 1 commit into
mainfrom
fy/rsdoctor-export-usage-graph

Conversation

@JSerFeng

@JSerFeng JSerFeng commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Expose an rsdoctor export usage graph that records which origin module export keeps which target module export active.

  • add exportUsageGraph support to the native rsdoctor plugin and JS hook API
  • collect candidate export-usage dependencies after finish_modules and filter active connections after dependency optimization
  • keep used_by_exports access scoped to rsdoctor by downcasting supported JavaScript dependency types instead of adding a core dependency trait API
  • add rsdoctor config cases for direct export usage and the EntryA -> bar -> baz class usage chain

Related links

N/A

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).

Validation:

  • cargo check -p rspack_plugin_rsdoctor -p rspack_binding_api
  • CARGO_BUILD_JOBS=1 pnpm run build:binding:dev
  • pnpm run build:js
  • cd tests/rspack-test && node ../../node_modules/@rstest/core/bin/rstest.js -t exportUsageClassGraph Config.part3.test.js
  • cd tests/rspack-test && node ../../node_modules/@rstest/core/bin/rstest.js -t rsdoctor Config.part3.test.js
  • cargo fmt --all --check

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

📦 Binary Size-limit

Comparing dd62a9f to feat: support rspack magic comment prefix (#14323) by AsyncIter

❌ Size increased by 56.00KB from 62.60MB to 62.65MB (⬆️0.09%)

@codspeed-hq

codspeed-hq Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will degrade performance by 22.47%

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

❌ 1 regressed benchmark
✅ 51 untouched benchmarks
⏩ 40 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
WallTime bundle@threejs-10x-development 237.1 ms 305.8 ms -22.47%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing fy/rsdoctor-export-usage-graph (dd62a9f) with main (b04d9f4)

Open in CodSpeed

Footnotes

  1. 40 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.

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Rsdoctor Bundle Diff Analysis

Found 5 projects in monorepo, 0 projects with changes.

📊 Quick Summary
Project Total Size Gzip Size Change Gzip Change
popular-libs 1.7 MB 551.3 KB 0 0
react-1k 822.8 KB 218.3 KB 0 0
ui-components 4.8 MB 1.4 MB 0 0
react-10k 5.6 MB 1.3 MB 0 0
react-5k 2.7 MB 669.1 KB 0 0

Generated by Rsdoctor GitHub Action

@JSerFeng JSerFeng marked this pull request as ready for review June 7, 2026 18:24
Copilot AI review requested due to automatic review settings June 7, 2026 18:24

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6c8aff903a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +424 to +427
module_graph
.connection_by_dependency_id(&dependency_id)
.is_some_and(|connection| {
connection.is_active(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Filter origin exports after dependency activation

When one import dependency is used by multiple exported symbols and only one of those exports is actually retained (for example import { bar } ...; export function foo(){bar()}; export function unusedFoo(){bar()} with only foo used), this keeps every candidate edge for that dependency because connection.is_active(...) only answers whether any export in used_by_exports keeps the connection alive. As a result the exported usage graph reports an active edge from the tree-shaken unusedFoo export too, so consumers cannot trust originExport to identify which origin export kept the target export active; the active filtering needs to also check each candidate.origin_export against the origin module's ExportsInfo.

Useful? React with 👍 / 👎.

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.

Pull request overview

This PR exposes a new rsdoctor export usage graph (origin export → target export) through the native rsdoctor plugin and the JS hook API, and adds test config cases to validate the produced edges.

Changes:

  • Add exportUsageGraph option + exportUsageGraph compilation hook plumbing across JS (@rspack/core), Rust rsdoctor plugin, and N-API bindings.
  • Collect export-usage candidate dependencies at finish_modules, filter for active connections at optimize_dependencies, and emit edges during optimize_chunk_modules.
  • Add rsdoctor configCases to validate direct export usage and an EntryA -> bar -> baz usage chain.

Reviewed changes

Copilot reviewed 22 out of 24 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/rspack-test/configCases/rsdoctor/exportUsageGraph/shared.js Adds shared exports used by the export-usage graph test case.
tests/rspack-test/configCases/rsdoctor/exportUsageGraph/lib.js Adds intermediate module to form a simple export usage chain.
tests/rspack-test/configCases/rsdoctor/exportUsageGraph/index.js Adds test entry asserting runtime behavior for the export usage graph case.
tests/rspack-test/configCases/rsdoctor/exportUsageGraph/rspack.config.js Adds config + hook assertions for exportUsageGraph edges.
tests/rspack-test/configCases/rsdoctor/exportUsageClassGraph/b.js Adds bar -> baz chain for class usage scenario.
tests/rspack-test/configCases/rsdoctor/exportUsageClassGraph/c.js Adds baz/unusedBaz exports used by class graph scenario.
tests/rspack-test/configCases/rsdoctor/exportUsageClassGraph/entryA.js Adds EntryA class that consumes bar() to drive export usage.
tests/rspack-test/configCases/rsdoctor/exportUsageClassGraph/index.js Adds test entry asserting EntryA behavior.
tests/rspack-test/configCases/rsdoctor/exportUsageClassGraph/rspack.config.js Adds config + assertions for class-chain export usage edges.
packages/rspack/src/builtin-plugin/RsdoctorPlugin.ts Exposes exportUsageGraph option and adds JS hook registration for it.
crates/rspack_plugin_rsdoctor/src/plugin.rs Implements collection/filter/emission lifecycle for export usage graph in Rust plugin.
crates/rspack_plugin_rsdoctor/src/module_graph.rs Adds export usage dependency collection, active filtering, and edge materialization.
crates/rspack_plugin_rsdoctor/src/data.rs Introduces rsdoctor export usage graph data structures.
crates/rspack_plugin_rsdoctor/src/drive.rs Adds new rsdoctor hook type for export usage graph.
crates/rspack_plugin_rsdoctor/src/lib.rs Re-exports new SendExportUsageGraph type.
crates/rspack_plugin_rsdoctor/Cargo.toml Adds dependency on rspack_plugin_javascript to access used_by_exports.
crates/rspack_plugin_javascript/src/dependency/url/mod.rs Exposes used_by_exports() getter on URLDependency.
crates/rspack_plugin_javascript/src/dependency/pure_expression_dependency.rs Exposes used_by_exports() getter on PureExpressionDependency.
crates/rspack_plugin_javascript/src/dependency/esm/esm_import_specifier_dependency.rs Exposes used_by_exports() getter on ESMImportSpecifierDependency.
crates/rspack_binding_api/src/rsdoctor.rs Adds N-API/TS-facing JsRsdoctorExportUsageGraph + edge types and option mapping.
crates/rspack_binding_api/src/plugins/js_hooks_plugin.rs Wires the new rsdoctor hook interceptor into the JS hooks adapter.
crates/rspack_binding_api/src/plugins/interceptor.rs Adds new tap kind + register plumbing and hook runner impl for export usage graph.
crates/node_binding/napi-binding.d.ts Updates generated TypeScript typings for the new hook/data and option.
Cargo.lock Adds lockfile entry for new crate dependency.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +75 to +80
#[derive(Debug, Default, Clone)]
pub struct RsdoctorExportUsageDependency {
pub dependency_id: String,
pub dependency_type: String,
pub user_request: String,
pub loc: Option<String>,
Comment on lines +463 to +467
target_module_identifier: dependency.target_module_identifier.to_string(),
target_module_path: target_rsd_module.path.clone(),
target_export: dependency.target_export,
active: true,
})
@JSerFeng JSerFeng force-pushed the fy/rsdoctor-export-usage-graph branch from 6c8aff9 to 2228024 Compare June 8, 2026 02:57

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2228024100

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

register_runtime_plugin_link_preload_taps: RegisterRuntimePluginLinkPreloadTaps,
register_runtime_plugin_link_prefetch_taps: RegisterRuntimePluginLinkPrefetchTaps,
register_rsdoctor_plugin_module_graph_taps: RegisterRsdoctorPluginModuleGraphTaps,
register_rsdoctor_plugin_export_usage_graph_taps: RegisterRsdoctorPluginExportUsageGraphTaps,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Clear the export usage hook cache

This new rsdoctor register is defined with cache = true, but JsHooksAdapterPlugin::clear_cache clears every other cached rsdoctor register except register_rsdoctor_plugin_export_usage_graph_taps. In watch/multi-compile runs, the first compilation's JS taps stay cached, so later compilations can reuse stale exportUsageGraph taps and retain/call callbacks from the wrong compilation instead of re-registering the current hooks.

Useful? React with 👍 / 👎.

@JSerFeng JSerFeng force-pushed the fy/rsdoctor-export-usage-graph branch 3 times, most recently from 551d9b9 to 0a125f8 Compare June 9, 2026 09:28

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0a125f88da

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if let Some(dependency) = dependency.downcast_ref::<PureExpressionDependency>() {
return Some((get_origin_exports(dependency.used_by_exports()), vec![None]));
}
None

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle re-export dependencies in usage graph

When a module re-exports from another module, e.g. export { foo as bar } from './lib', the edge to ./lib is represented by EsmExportImportedSpecifier, not by an import specifier, URL, or pure-expression dependency. Falling through to None here means collect_export_usage_dependencies emits no edge from the barrel/re-export module to the real source module, so consumers of exportUsageEdges see the graph stop at the re-export and miss the actual target export used through common barrel files.

Useful? React with 👍 / 👎.

@JSerFeng JSerFeng force-pushed the fy/rsdoctor-export-usage-graph branch 2 times, most recently from 6b12109 to b283fdc Compare June 9, 2026 10:22
@JSerFeng JSerFeng force-pushed the fy/rsdoctor-export-usage-graph branch from b283fdc to dd62a9f Compare June 9, 2026 15:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants