feat(rsdoctor): expose export usage graph#14291
Conversation
📦 Binary Size-limit
❌ Size increased by 56.00KB from 62.60MB to 62.65MB (⬆️0.09%) |
Merging this PR will degrade performance by 22.47%
|
| 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)
Footnotes
-
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. ↩
Rsdoctor Bundle Diff AnalysisFound 5 projects in monorepo, 0 projects with changes. 📊 Quick Summary
Generated by Rsdoctor GitHub Action |
There was a problem hiding this comment.
💡 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".
| module_graph | ||
| .connection_by_dependency_id(&dependency_id) | ||
| .is_some_and(|connection| { | ||
| connection.is_active( |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
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
exportUsageGraphoption +exportUsageGraphcompilation 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 atoptimize_dependencies, and emit edges duringoptimize_chunk_modules. - Add rsdoctor configCases to validate direct export usage and an
EntryA -> bar -> bazusage 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.
| #[derive(Debug, Default, Clone)] | ||
| pub struct RsdoctorExportUsageDependency { | ||
| pub dependency_id: String, | ||
| pub dependency_type: String, | ||
| pub user_request: String, | ||
| pub loc: Option<String>, |
| target_module_identifier: dependency.target_module_identifier.to_string(), | ||
| target_module_path: target_rsd_module.path.clone(), | ||
| target_export: dependency.target_export, | ||
| active: true, | ||
| }) |
6c8aff9 to
2228024
Compare
There was a problem hiding this comment.
💡 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, |
There was a problem hiding this comment.
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 👍 / 👎.
551d9b9 to
0a125f8
Compare
There was a problem hiding this comment.
💡 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 |
There was a problem hiding this comment.
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 👍 / 👎.
6b12109 to
b283fdc
Compare
b283fdc to
dd62a9f
Compare
Summary
Expose an rsdoctor export usage graph that records which origin module export keeps which target module export active.
exportUsageGraphsupport to the native rsdoctor plugin and JS hook APIfinish_modulesand filter active connections after dependency optimizationused_by_exportsaccess scoped to rsdoctor by downcasting supported JavaScript dependency types instead of adding a core dependency trait APIEntryA -> bar -> bazclass usage chainRelated links
N/A
Checklist
Validation:
cargo check -p rspack_plugin_rsdoctor -p rspack_binding_apiCARGO_BUILD_JOBS=1 pnpm run build:binding:devpnpm run build:jscd tests/rspack-test && node ../../node_modules/@rstest/core/bin/rstest.js -t exportUsageClassGraph Config.part3.test.jscd tests/rspack-test && node ../../node_modules/@rstest/core/bin/rstest.js -t rsdoctor Config.part3.test.jscargo fmt --all --check