Repository: web-infra-dev/rspack
Pull request #10531: feat: support dependences in build info
Author: login: SyMind
Labels: name: team
description: The issue/pr is created by the member of Rspack.
name: release: feature
description: release: feature related release(mr only)
Created: 2025-05-30T09:23:06Z | Merged: 2025-05-30T10:52:02Z

<!--
  Thank you for submitting a pull request!

  We appreciate the time and effort you have invested in making these changes. Please ensure that you provide enough information to allow others to review your pull request.

  Upon submission, your pull request will be automatically assigned with reviewers.

  If you want to learn more about contributing to this project, please visit: https://github.com/web-infra-dev/rspack/blob/main/CONTRIBUTING.md.
-->

## Summary

<!-- Can you explain the reasoning behind implementing this change? What problem or issue does this pull request resolve? -->

<!-- It would be helpful if you could provide any relevant context, such as GitHub issues or related discussions. -->

Support follow js api:
- buildInfo.fileDependencies
- buildInfo.contextDependencies
- buildInfo.missingDependencies
- buildInfo.buildDependencies

And fix the module.buildInfo typescript type.

## Checklist

<!--- Check and mark with an "x" -->

- [ ] Tests updated (or not required).
- [ ] Documentation updated (or not required).



Diff:
diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts
index b08868ec92f4..3304b649413c 100644
--- a/crates/node_binding/binding.d.ts
+++ b/crates/node_binding/binding.d.ts
@@ -24,8 +24,8 @@ export interface Module {
 	get useSourceMap(): boolean;
 	get useSimpleSourceMap(): boolean;
 	get _readableIdentifier(): string;
-	buildInfo: Record<string, any>;
-	buildMeta: Record<string, any>;
+	buildInfo: KnownBuildInfo & Record<string, any>;
+	buildMeta: KnownBuildInfo & Record<string, any>;
 }
 
 interface NormalModuleConstructor {
@@ -74,8 +74,12 @@ export declare class AsyncDependenciesBlock {
   get blocks(): AsyncDependenciesBlock[]
 }
 
-export declare class BuildInfo {
+export declare class KnownBuildInfo {
   get _assets(): Assets
+  get _fileDependencies(): Array<string>
+  get _contextDependencies(): Array<string>
+  get _missingDependencies(): Array<string>
+  get _buildDependencies(): Array<string>
 }
 
 export declare class Chunks {
diff --git a/crates/node_binding/scripts/banner.d.ts b/crates/node_binding/scripts/banner.d.ts
index 89b1bd4a2c5a..415cae1bb78a 100644
--- a/crates/node_binding/scripts/banner.d.ts
+++ b/crates/node_binding/scripts/banner.d.ts
@@ -24,8 +24,8 @@ export interface Module {
 	get useSourceMap(): boolean;
 	get useSimpleSourceMap(): boolean;
 	get _readableIdentifier(): string;
-	buildInfo: Record<string, any>;
-	buildMeta: Record<string, any>;
+	buildInfo: KnownBuildInfo & Record<string, any>;
+	buildMeta: KnownBuildInfo & Record<string, any>;
 }
 
 interface NormalModuleConstructor {
diff --git a/crates/node_binding/src/build_info.rs b/crates/node_binding/src/build_info.rs
index 5785e6caac26..670b3bda3b02 100644
--- a/crates/node_binding/src/build_info.rs
+++ b/crates/node_binding/src/build_info.rs
@@ -45,11 +45,11 @@ impl Assets {
 }
 
 #[napi]
-pub struct BuildInfo {
+pub struct KnownBuildInfo {
   module_reference: WeakReference<Module>,
 }
 
-impl BuildInfo {
+impl KnownBuildInfo {
   pub fn new(module_reference: WeakReference<Module>) -> Self {
     Self { module_reference }
   }
@@ -78,9 +78,57 @@ impl BuildInfo {
 }
 
 #[napi]
-impl BuildInfo {
+impl KnownBuildInfo {
   #[napi(getter, js_name = "_assets", ts_return_type = "Assets")]
   pub fn assets(&mut self) -> napi::Result<Reflector> {
     self.with_ref(|module| Ok(module.build_info().assets.reflector()))
   }
+
+  #[napi(getter, js_name = "_fileDependencies")]
+  pub fn file_dependencies<'a>(&mut self, env: &'a Env) -> napi::Result<Vec<JsString<'a>>> {
+    self.with_ref(|module| {
+      module
+        .build_info()
+        .file_dependencies
+        .iter()
+        .map(|dependency| env.create_string(dependency.to_string_lossy().as_ref()))
+        .collect::<napi::Result<Vec<JsString>>>()
+    })
+  }
+
+  #[napi(getter, js_name = "_contextDependencies")]
+  pub fn context_dependencies<'a>(&mut self, env: &'a Env) -> napi::Result<Vec<JsString<'a>>> {
+    self.with_ref(|module| {
+      module
+        .build_info()
+        .context_dependencies
+        .iter()
+        .map(|dependency| env.create_string(dependency.to_string_lossy().as_ref()))
+        .collect::<napi::Result<Vec<JsString>>>()
+    })
+  }
+
+  #[napi(getter, js_name = "_missingDependencies")]
+  pub fn missing_dependencies<'a>(&mut self, env: &'a Env) -> napi::Result<Vec<JsString<'a>>> {
+    self.with_ref(|module| {
+      module
+        .build_info()
+        .missing_dependencies
+        .iter()
+        .map(|dependency| env.create_string(dependency.to_string_lossy().as_ref()))
+        .collect::<napi::Result<Vec<JsString>>>()
+    })
+  }
+
+  #[napi(getter, js_name = "_buildDependencies")]
+  pub fn build_dependencies<'a>(&mut self, env: &'a Env) -> napi::Result<Vec<JsString<'a>>> {
+    self.with_ref(|module| {
+      module
+        .build_info()
+        .build_dependencies
+        .iter()
+        .map(|dependency| env.create_string(dependency.to_string_lossy().as_ref()))
+        .collect::<napi::Result<Vec<JsString>>>()
+    })
+  }
 }
diff --git a/crates/node_binding/src/module.rs b/crates/node_binding/src/module.rs
index ee8e5a38f36e..8f5c31ffa61c 100644
--- a/crates/node_binding/src/module.rs
+++ b/crates/node_binding/src/module.rs
@@ -18,9 +18,9 @@ use rspack_util::source_map::SourceMapKind;
 
 use super::JsCompatSourceOwned;
 use crate::{
-  AssetInfo, AsyncDependenciesBlockWrapper, BuildInfo, ConcatenatedModule, ContextModule,
-  DependencyWrapper, ExternalModule, JsChunkWrapper, JsCodegenerationResults, JsCompatSource,
-  JsCompiler, NormalModule, ToJsCompatSource, COMPILER_REFERENCES,
+  AssetInfo, AsyncDependenciesBlockWrapper, ConcatenatedModule, ContextModule, DependencyWrapper,
+  ExternalModule, JsChunkWrapper, JsCodegenerationResults, JsCompatSource, JsCompiler,
+  KnownBuildInfo, NormalModule, ToJsCompatSource, COMPILER_REFERENCES,
 };
 
 #[napi(object)]
@@ -149,7 +149,7 @@ impl Module {
       if let Some(r) = &reference.build_info_ref {
         return r.as_object(env);
       }
-      let mut build_info = BuildInfo::new(reference.downgrade()).get_jsobject(env)?;
+      let mut build_info = KnownBuildInfo::new(reference.downgrade()).get_jsobject(env)?;
       MODULE_BUILD_INFO_SYMBOL.with(|once_cell| {
         let sym = unsafe {
           #[allow(clippy::unwrap_used)]
@@ -172,7 +172,7 @@ impl Module {
       let raw_env = env.raw();
       let mut reference: Reference<Module> =
         unsafe { Reference::from_napi_value(raw_env, this.raw())? };
-      let new_build_info = BuildInfo::new(reference.downgrade());
+      let new_build_info = KnownBuildInfo::new(reference.downgrade());
       let mut new_instrance = new_build_info.get_jsobject(env)?;
 
       let names = input_object.get_all_property_names(
diff --git a/crates/node_binding/src/modules/macros.rs b/crates/node_binding/src/modules/macros.rs
index 9617bcd45f1b..06d3f8d05814 100644
--- a/crates/node_binding/src/modules/macros.rs
+++ b/crates/node_binding/src/modules/macros.rs
@@ -141,7 +141,7 @@ macro_rules! impl_module_methods {
           if let Some(r) = &reference.build_info_ref {
             return r.as_object(env);
           }
-          let mut build_info = $crate::BuildInfo::new(reference.downgrade()).get_jsobject(env)?;
+          let mut build_info = $crate::KnownBuildInfo::new(reference.downgrade()).get_jsobject(env)?;
           $crate::MODULE_BUILD_INFO_SYMBOL.with(|once_cell| {
             let sym = unsafe {
               #[allow(clippy::unwrap_used)]
@@ -166,7 +166,7 @@ macro_rules! impl_module_methods {
           let raw_env = env.raw();
           let mut reference: napi::bindgen_prelude::Reference<Module> =
             unsafe { napi::bindgen_prelude::Reference::from_napi_value(raw_env, this.raw())? };
-          let new_build_info = $crate::BuildInfo::new(reference.downgrade());
+          let new_build_info = $crate::KnownBuildInfo::new(reference.downgrade());
           let mut new_instrance = new_build_info.get_jsobject(env)?;
 
           let names = input_object.get_all_property_names(
diff --git a/packages/rspack-test-tools/tests/configCases/module/build-info/build.txt b/packages/rspack-test-tools/tests/configCases/module/build-info/build.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/rspack-test-tools/tests/configCases/module/build-info/loader.js b/packages/rspack-test-tools/tests/configCases/module/build-info/loader.js
index 7f447f036280..5314736e44cb 100644
--- a/packages/rspack-test-tools/tests/configCases/module/build-info/loader.js
+++ b/packages/rspack-test-tools/tests/configCases/module/build-info/loader.js
@@ -2,5 +2,6 @@ module.exports = function (content) {
     this._module.buildInfo.loaded = true;
 
     this.emitFile("foo.txt", "foo");
+    this.addBuildDependency("./build.txt");
     return content;
 };
diff --git a/packages/rspack-test-tools/tests/configCases/module/build-info/rspack.config.js b/packages/rspack-test-tools/tests/configCases/module/build-info/rspack.config.js
index aa79c3ec5463..ec86c06e56cc 100644
--- a/packages/rspack-test-tools/tests/configCases/module/build-info/rspack.config.js
+++ b/packages/rspack-test-tools/tests/configCases/module/build-info/rspack.config.js
@@ -10,6 +10,16 @@ class Plugin {
 
 			// known build info
 			expect(Object.keys(entryModule.buildInfo.assets)).toContain("foo.txt");
+
+			expect(entryModule.buildInfo.fileDependencies.size).toBe(1);
+			expect(entryModule.buildInfo.fileDependencies.has(path.join(__dirname, "index.js"))).toBe(true);
+
+			expect(entryModule.buildInfo.buildDependencies.size).toBe(1);
+			expect(entryModule.buildInfo.buildDependencies.has("./build.txt")).toBe(true);
+
+			expect(entryModule.buildInfo.contextDependencies.size).toBe(0);
+
+			expect(entryModule.buildInfo.missingDependencies.size).toBe(0);
 		});
 	}
 }
diff --git a/packages/rspack/src/Module.ts b/packages/rspack/src/Module.ts
index 4d335769e66c..86e2b0e44f80 100644
--- a/packages/rspack/src/Module.ts
+++ b/packages/rspack/src/Module.ts
@@ -183,15 +183,19 @@ declare module "@rspack/binding" {
 		[$assets]: Record<string, Source>;
 	}
 
-	interface BuildInfo {
+	interface KnownBuildInfo {
 		assets: Record<string, Source>;
+		fileDependencies: Set<string>;
+		contextDependencies: Set<string>;
+		missingDependencies: Set<string>;
+		buildDependencies: Set<string>;
 	}
 }
 
-Object.defineProperty(binding.BuildInfo.prototype, "assets", {
+Object.defineProperty(binding.KnownBuildInfo.prototype, "assets", {
 	enumerable: true,
 	configurable: true,
-	get(this: binding.BuildInfo): Record<string, Source> {
+	get(this: binding.KnownBuildInfo): Record<string, Source> {
 		if (this._assets[$assets]) {
 			return this._assets[$assets];
 		}
@@ -215,6 +219,38 @@ Object.defineProperty(binding.BuildInfo.prototype, "assets", {
 	}
 });
 
+Object.defineProperty(binding.KnownBuildInfo.prototype, "fileDependencies", {
+	enumerable: true,
+	configurable: true,
+	get(this: binding.KnownBuildInfo): Set<string> {
+		return new Set(this._fileDependencies);
+	}
+});
+
+Object.defineProperty(binding.KnownBuildInfo.prototype, "contextDependencies", {
+	enumerable: true,
+	configurable: true,
+	get(this: binding.KnownBuildInfo): Set<string> {
+		return new Set(this._contextDependencies);
+	}
+});
+
+Object.defineProperty(binding.KnownBuildInfo.prototype, "missingDependencies", {
+	enumerable: true,
+	configurable: true,
+	get(this: binding.KnownBuildInfo): Set<string> {
+		return new Set(this._missingDependencies);
+	}
+});
+
+Object.defineProperty(binding.KnownBuildInfo.prototype, "buildDependencies", {
+	enumerable: true,
+	configurable: true,
+	get(this: binding.KnownBuildInfo): Set<string> {
+		return new Set(this._buildDependencies);
+	}
+});
+
 Object.defineProperty(binding.Module.prototype, "identifier", {
 	enumerable: true,
 	configurable: true,


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR adds support for dependency tracking properties on `BuildInfo`, updates the underlying Rust bindings, and extends tests to verify the new properties.

- Extend `BuildInfo` with `fileDependencies`, `contextDependencies`, `missingDependencies`, and `buildDependencies` in TS and JS getters.
- Implement matching napi getters in the Rust binding.
- Update loader and tests to add and assert on `buildDependencies` (and empty context/missing sets).

### Reviewed Changes

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

| File                                                                                         | Description                                                                    |
| -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| packages/rspack/src/Module.ts                                                                | Added new `BuildInfo` interface fields and JS getters for dependency sets      |
| packages/rspack-test-tools/tests/configCases/module/build-info/rspack.config.js              | Added assertions for `fileDependencies`, `buildDependencies`, `contextDependencies`, and `missingDependencies` |
| packages/rspack-test-tools/tests/configCases/module/build-info/loader.js                     | Updated loader to call `this.addBuildDependency("./build.txt")`                |
| crates/node_binding/src/build_info.rs                                                        | Added napi getters for `_fileDependencies`, `_contextDependencies`, `_missingDependencies`, and `_buildDependencies` |


<details>
<summary>Comments suppressed due to low confidence (1)</summary>

**packages/rspack-test-tools/tests/configCases/module/build-info/rspack.config.js:20**
* Currently only the empty case is tested for `contextDependencies` and `missingDependencies`. Add tests that call `this.addContextDependency(...)` and `this.addMissingDependency(...)` in the loader to verify non-empty scenarios.
```
expect(entryModule.buildInfo.contextDependencies.size).toBe(0);
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-30T09:27:26Z
user: login: jerrykingxyz
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-30T10:41:25Z

Review comments:
diff_hunk: @@ -215,6 +219,38 @@ Object.defineProperty(binding.BuildInfo.prototype, "assets", {
 	}
 });
 
+Object.defineProperty(binding.BuildInfo.prototype, "fileDependencies", {
path: packages/rspack/src/Module.ts
user: login: Copilot
body: [nitpick] The repeated Object.defineProperty calls for each dependency property introduce boilerplate. Consider extracting a helper function or iterating over an array of property names to define these getters in a loop and reduce duplication.
created_at: 2025-05-30T09:27:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -185,6 +185,10 @@ declare module "@rspack/binding" {
 
 	interface BuildInfo {
 		assets: Record<string, Source>;
+		fileDependencies: Set<string>;
path: packages/rspack/src/Module.ts
user: login: Copilot
body: Add a JSDoc comment above this new `fileDependencies` field (and the others) in the `BuildInfo` interface to explain what each set contains and when they are populated.
created_at: 2025-05-30T09:27:26Z
author_association: CONTRIBUTOR

PR comments:
user: login: netlify[bot]
created_at: 2025-05-30T09:23:11Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *rspack* canceled.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 531078d9404d98b9bc9943952fe4722cde2a38e7 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/projects/rspack/deploys/68397a969e3ae800084d906c |
author_association: NONE
user: login: codspeed-hq[bot]
created_at: 2025-05-30T09:43:36Z
body: <!-- __CODSPEED_PERFORMANCE_REPORT_COMMENT__ -->
<!-- __CODSPEED_INSTRUMENTATION_PERFORMANCE_REPORT_COMMENT__ -->

## [CodSpeed Performance Report](https://codspeed.io/web-infra-dev/rspack/branches/build-info-add-dependencies)

### Merging #10531 will **not alter performance**

<sub>Comparing <code>build-info-add-dependencies</code> (531078d) with <code>main</code> (18795b8)</sub>


#### :tada: Hooray! `codspeed-rust` just leveled up to 2.7.2!

> A heads-up, this is a **breaking change** and it might affect your current performance baseline a bit. But here's the exciting part - it's packed with new, cool features and promises improved result stability :partying_face:!
> _Curious about what's new? Visit our [releases](https://github.com/CodSpeedHQ/codspeed-rust/releases) page to delve into all the awesome details about this new version._

### Summary

`✅ 12` untouched benchmarks  




author_association: NONE