Skip to content

Commit 09af1d8

Browse files
jerrykingxyzahabhgk
andauthoredFeb 9, 2025··
fix: incremental make rebuild removed entry module (#9209)
* test: add test for make rebuild removed entry module * fix: incremental make rebuild removed entry module * fix: incremental make rebuild removed entry module --------- Co-authored-by: ahabhgk <ahabhgk@gmail.com>
1 parent ccbb924 commit 09af1d8

File tree

10 files changed

+136
-67
lines changed

10 files changed

+136
-67
lines changed
 

‎crates/rspack_core/src/cache/persistent/occasion/make/module_graph.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ pub async fn recovery_module_graph(
187187
if module_exist {
188188
None
189189
} else {
190-
mg.revoke_connection(dep_id, false)
190+
mg.revoke_dependency(dep_id, false)
191191
}
192192
})
193193
.collect::<HashSet<_>>();

‎crates/rspack_core/src/compiler/compilation.rs

-1
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,6 @@ impl Compilation {
821821
#[instrument("Compilation:make", skip_all)]
822822
pub async fn make(&mut self) -> Result<()> {
823823
self.make_artifact.reset_dependencies_incremental_info();
824-
// self.module_executor.
825824
// run module_executor
826825
if let Some(module_executor) = &mut self.module_executor {
827826
let mut module_executor = std::mem::take(module_executor);

‎crates/rspack_core/src/compiler/make/cutout/mod.rs

+57-38
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ mod fix_build_meta;
33
mod fix_issuers;
44
mod has_module_graph_change;
55

6-
use rspack_collections::IdentifierSet;
6+
use rspack_collections::{IdentifierSet, UkeySet};
77
use rustc_hash::FxHashSet as HashSet;
88

99
use self::{
@@ -27,33 +27,22 @@ impl Cutout {
2727
artifact: &mut MakeArtifact,
2828
params: Vec<MakeParam>,
2929
) -> HashSet<BuildDependency> {
30-
let mut entry_dependencies = std::mem::take(&mut artifact.entry_dependencies);
30+
let mut next_entry_dependencies = HashSet::default();
31+
let mut clean_entry_dependencies = false;
32+
let mut useless_entry_dependencies = UkeySet::default();
3133
let mut force_build_modules = IdentifierSet::default();
3234
let mut force_build_deps = HashSet::default();
33-
let mut remove_entry_deps = HashSet::default();
3435

3536
let module_graph = artifact.get_module_graph();
3637

3738
for item in params {
3839
match item {
3940
MakeParam::BuildEntry(deps) => {
40-
for dep_id in deps {
41-
if !entry_dependencies.contains(&dep_id) {
42-
force_build_deps.insert((dep_id, None));
43-
entry_dependencies.insert(dep_id);
44-
}
45-
}
41+
next_entry_dependencies = deps;
4642
}
4743
MakeParam::BuildEntryAndClean(deps) => {
48-
remove_entry_deps.extend(std::mem::take(&mut entry_dependencies));
49-
entry_dependencies = deps;
50-
for dep_id in &entry_dependencies {
51-
if remove_entry_deps.contains(dep_id) {
52-
remove_entry_deps.remove(dep_id);
53-
} else {
54-
force_build_deps.insert((*dep_id, None));
55-
}
56-
}
44+
next_entry_dependencies = deps;
45+
clean_entry_dependencies = true;
5746
}
5847
MakeParam::CheckNeedBuild => {
5948
force_build_modules.extend(module_graph.modules().values().filter_map(|module| {
@@ -65,32 +54,30 @@ impl Cutout {
6554
}));
6655
}
6756
MakeParam::ModifiedFiles(files) => {
68-
force_build_modules.extend(module_graph.modules().values().filter_map(|module| {
57+
for module in module_graph.modules().values() {
6958
// check has dependencies modified
7059
if module.depends_on(&files) {
71-
Some(module.identifier())
72-
} else {
73-
None
60+
// add module id
61+
force_build_modules.insert(module.identifier());
7462
}
75-
}))
63+
}
7664
}
7765
MakeParam::RemovedFiles(files) => {
78-
force_build_modules.extend(module_graph.modules().values().flat_map(|module| {
79-
let mut res = vec![];
80-
66+
for module in module_graph.modules().values() {
8167
// check has dependencies modified
8268
if module.depends_on(&files) {
8369
// add module id
84-
res.push(module.identifier());
85-
// add parent module id
86-
res.extend(
87-
module_graph
88-
.get_incoming_connections(&module.identifier())
89-
.filter_map(|connect| connect.original_module_identifier),
90-
)
70+
force_build_modules.insert(module.identifier());
71+
// process parent module id
72+
for connect in module_graph.get_incoming_connections(&module.identifier()) {
73+
if let Some(original_module_identifier) = connect.original_module_identifier {
74+
force_build_modules.insert(original_module_identifier);
75+
} else {
76+
useless_entry_dependencies.insert(connect.dependency_id);
77+
}
78+
}
9179
}
92-
res
93-
}))
80+
}
9481
}
9582
MakeParam::ForceBuildDeps(deps) => {
9683
for item in deps {
@@ -128,23 +115,55 @@ impl Cutout {
128115
force_build_deps.extend(artifact.revoke_module(&id));
129116
}
130117

118+
let mut entry_dependencies = std::mem::take(&mut artifact.entry_dependencies);
131119
let mut module_graph = artifact.get_module_graph_mut();
132-
for dep_id in remove_entry_deps {
120+
// remove useless entry dependencies
121+
let mut remove_entry_dependencies = HashSet::default();
122+
for dep_id in useless_entry_dependencies {
123+
if !next_entry_dependencies.contains(&dep_id) {
124+
remove_entry_dependencies.insert(dep_id);
125+
}
126+
}
127+
if clean_entry_dependencies {
128+
remove_entry_dependencies.extend(entry_dependencies.difference(&next_entry_dependencies));
129+
}
130+
for dep_id in remove_entry_dependencies {
133131
// connection may have been deleted by revoke module
134132
if let Some(con) = module_graph.connection_by_dependency_id(&dep_id) {
133+
// need clean_isolated_module to check whether the module is still used by other deps
135134
self
136135
.clean_isolated_module
137136
.add_need_check_module(*con.module_identifier());
138-
module_graph.revoke_connection(&dep_id, true);
139137
}
140-
force_build_deps.remove(&(dep_id, None));
138+
module_graph.revoke_dependency(&dep_id, true);
139+
entry_dependencies.remove(&dep_id);
140+
}
141+
142+
// add entry dependencies
143+
for dep in next_entry_dependencies
144+
.difference(&entry_dependencies)
145+
.copied()
146+
.collect::<Vec<_>>()
147+
{
148+
force_build_deps.insert((dep, None));
149+
entry_dependencies.insert(dep);
141150
}
142151

143152
artifact.entry_dependencies = entry_dependencies;
144153

145154
self.has_module_graph_change.analyze_artifact(artifact);
146155

156+
// only return available force_build_deps
157+
let module_graph = artifact.get_module_graph();
147158
force_build_deps
159+
.into_iter()
160+
.filter(|(dep_id, _)| {
161+
let Some(dep) = module_graph.dependency_by_id(dep_id) else {
162+
return false;
163+
};
164+
dep.as_module_dependency().is_some() || dep.as_context_dependency().is_some()
165+
})
166+
.collect()
148167
}
149168

150169
pub fn fix_artifact(self, artifact: &mut MakeArtifact) {

‎crates/rspack_core/src/compiler/make/repair/mod.rs

+3-12
Original file line numberDiff line numberDiff line change
@@ -102,22 +102,13 @@ pub async fn repair(
102102
let module_graph = artifact.get_module_graph_mut();
103103
let init_tasks = build_dependencies
104104
.into_iter()
105-
.filter_map::<Box<dyn Task<MakeTaskContext>>, _>(|(id, parent_module_identifier)| {
105+
.map::<Box<dyn Task<MakeTaskContext>>, _>(|(id, parent_module_identifier)| {
106106
let dependency = module_graph
107107
.dependency_by_id(&id)
108108
.expect("dependency not found");
109-
// filter module_dependency and context_dependency
110-
if dependency.as_module_dependency().is_none() && dependency.as_context_dependency().is_none()
111-
{
112-
return None;
113-
}
114109

115-
// filter parent module existed dependency
116110
let parent_module =
117111
parent_module_identifier.and_then(|id| module_graph.module_by_identifier(&id));
118-
if parent_module_identifier.is_some() && parent_module.is_none() {
119-
return None;
120-
}
121112

122113
let current_profile = compilation
123114
.options
@@ -134,7 +125,7 @@ pub async fn repair(
134125
None
135126
}
136127
});
137-
Some(Box::new(factorize::FactorizeTask {
128+
Box::new(factorize::FactorizeTask {
138129
compilation_id: compilation.id(),
139130
module_factory: compilation.get_dependency_factory(dependency),
140131
original_module_identifier: parent_module_identifier,
@@ -149,7 +140,7 @@ pub async fn repair(
149140
options: compilation.options.clone(),
150141
current_profile,
151142
resolver_factory: compilation.resolver_factory.clone(),
152-
}))
143+
})
153144
})
154145
.collect::<Vec<_>>();
155146

‎crates/rspack_core/src/module_graph/mod.rs

+19-13
Original file line numberDiff line numberDiff line change
@@ -213,35 +213,41 @@ impl<'a> ModuleGraph<'a> {
213213
map
214214
}
215215

216-
/// Remove a connection and return connection origin module identifier and dependency
216+
/// Remove dependency in mgm and target connection, return dependency_id and origin module identifier
217217
///
218218
/// force will completely remove dependency, and you will not regenerate it from dependency_id
219-
pub fn revoke_connection(
219+
pub fn revoke_dependency(
220220
&mut self,
221221
dep_id: &DependencyId,
222222
force: bool,
223223
) -> Option<BuildDependency> {
224-
let connection = self.connection_by_dependency_id(dep_id)?;
225-
let module_identifier = *connection.module_identifier();
226-
let original_module_identifier = connection.original_module_identifier;
224+
let connection_info = self.connection_by_dependency_id(dep_id).map(|connection| {
225+
(
226+
connection.original_module_identifier,
227+
*connection.module_identifier(),
228+
)
229+
});
227230

228231
let Some(active_partial) = &mut self.active else {
229232
panic!("should have active partial");
230233
};
231-
active_partial.connections.insert(*dep_id, None);
234+
if connection_info.is_some() {
235+
active_partial.connections.insert(*dep_id, None);
236+
}
232237
if force {
233238
active_partial.dependencies.insert(*dep_id, None);
234239
active_partial
235240
.dependency_id_to_parents
236241
.insert(*dep_id, None);
237242
}
238243

244+
let (original_module_identifier, module_identifier) = connection_info?;
239245
// remove outgoing from original module graph module
240246
if let Some(original_module_identifier) = &original_module_identifier {
241247
if let Some(mgm) = self.module_graph_module_by_identifier_mut(original_module_identifier) {
242248
mgm.remove_outgoing_connection(dep_id);
243-
// Because of mgm.dependencies is set when original module build success
244-
// it does not need to remove dependency in mgm.dependencies.
249+
// Because of mgm.all_dependencies is set when original module build success
250+
// it does not need to remove dependency in mgm.all_dependencies.
245251
}
246252
}
247253
// remove incoming from module graph module
@@ -258,12 +264,12 @@ impl<'a> ModuleGraph<'a> {
258264
.map(|m| Vec::from(m.get_blocks()))
259265
.unwrap_or_default();
260266

261-
let (outgoing_connections, incoming_connections) = self
267+
let (incoming_connections, all_dependencies) = self
262268
.module_graph_module_by_identifier(module_id)
263269
.map(|mgm| {
264270
(
265-
mgm.outgoing_connections().clone(),
266271
mgm.incoming_connections().clone(),
272+
mgm.all_dependencies.clone(),
267273
)
268274
})
269275
.unwrap_or_default();
@@ -279,13 +285,13 @@ impl<'a> ModuleGraph<'a> {
279285
active_partial.blocks.insert(block, None);
280286
}
281287

282-
for cid in outgoing_connections {
283-
self.revoke_connection(&cid, true);
288+
for dep_id in all_dependencies {
289+
self.revoke_dependency(&dep_id, true);
284290
}
285291

286292
incoming_connections
287293
.iter()
288-
.filter_map(|cid| self.revoke_connection(cid, false))
294+
.filter_map(|dep_id| self.revoke_dependency(dep_id, false))
289295
.collect()
290296
}
291297

‎crates/rspack_plugin_library/src/modern_module_library_plugin.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ async fn finish_modules(&self, compilation: &mut Compilation) -> Result<()> {
273273
let boxed_dep = Box::new(new_dep.clone()) as BoxDependency;
274274
block.add_dependency_id(*new_dep.id());
275275
mg.add_dependency(boxed_dep);
276-
mg.revoke_connection(connection_id, true);
276+
mg.revoke_dependency(connection_id, true);
277277
}
278278
}
279279

@@ -380,7 +380,7 @@ async fn finish_modules(&self, compilation: &mut Compilation) -> Result<()> {
380380
.expect("should have module");
381381

382382
importer.remove_dependency_id(*connection);
383-
mg.revoke_connection(connection, true);
383+
mg.revoke_dependency(connection, true);
384384
}
385385
}
386386

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { expect, test } from "@/fixtures";
2+
3+
test("should compile", async ({ page, fileAction, rspack }) => {
4+
await expect(page.getByText("index1")).toBeVisible();
5+
await expect(page.getByText("index2")).toBeVisible();
6+
7+
fileAction.deleteFile("src/index2.js");
8+
fileAction.updateFile("src/index1.js", content => content.replace("div.innerText = \"index1\";", "div.innerText = \"index1 updated\";"));
9+
10+
await expect(async () => {
11+
await page.reload();
12+
expect(await page.locator("#index1").innerText()).toBe("index1 updated");
13+
}).toPass();
14+
await expect(page.locator("#index2")).toHaveCount(0);
15+
await expect(page.locator("#webpack-dev-server-client-overlay")).toHaveCount(0);
16+
17+
const stats = rspack.compiler._lastCompilation?.getStats().toJson({ all: false, errors: true });
18+
expect(stats?.errors?.length).toBe(0);
19+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const path = require("path")
2+
const fs = require("fs");
3+
const rspack = require("@rspack/core");
4+
5+
/** @type {import("@rspack/core").Configuration} */
6+
module.exports = {
7+
entry: async () => {
8+
const context = path.resolve(__dirname, "src");
9+
const files = await fs.promises.readdir(context);
10+
let entries = files.filter(f => f.startsWith('index'));
11+
entries.sort();
12+
return entries.reduce((acc, e, i) => {
13+
acc[`index${i + 1}`] = path.resolve(context, e);
14+
return acc;
15+
}, {})
16+
},
17+
context: __dirname,
18+
mode: "development",
19+
plugins: [
20+
new rspack.HtmlRspackPlugin(),
21+
],
22+
devServer: {
23+
hot: true
24+
},
25+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Promise.resolve().then(() => {
2+
const div = document.createElement("div");
3+
div.innerText = "index1";
4+
div.id = "index1";
5+
document.body.appendChild(div);
6+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const div = document.createElement("div");
2+
div.innerText = "index2";
3+
div.id = "index2";
4+
document.body.appendChild(div);

2 commit comments

Comments
 (2)

github-actions[bot] commented on Feb 9, 2025

@github-actions[bot]
Contributor

📝 Benchmark detail: Open

Name Base (2025-02-09 ccbb924) Current Change
10000_big_production-mode_disable-minimize + exec 38.4 s ± 903 ms 38.9 s ± 191 ms +1.42 %
10000_development-mode + exec 1.84 s ± 41 ms 1.85 s ± 46 ms +0.72 %
10000_development-mode_hmr + exec 693 ms ± 17 ms 691 ms ± 1.5 ms -0.22 %
10000_production-mode + exec 2.32 s ± 150 ms 2.27 s ± 32 ms -2.16 %
10000_production-mode_persistent-cold + exec 2.48 s ± 70 ms 2.45 s ± 148 ms -1.19 %
10000_production-mode_persistent-hot + exec 1.66 s ± 54 ms 1.65 s ± 35 ms -0.54 %
arco-pro_development-mode + exec 1.74 s ± 161 ms 1.78 s ± 201 ms +2.11 %
arco-pro_development-mode_hmr + exec 389 ms ± 2.7 ms 388 ms ± 3.6 ms -0.44 %
arco-pro_production-mode + exec 3.65 s ± 234 ms 3.63 s ± 171 ms -0.41 %
arco-pro_production-mode_generate-package-json-webpack-plugin + exec 3.67 s ± 161 ms 3.68 s ± 103 ms +0.33 %
arco-pro_production-mode_persistent-cold + exec 3.84 s ± 181 ms 3.88 s ± 281 ms +1.06 %
arco-pro_production-mode_persistent-hot + exec 2.36 s ± 118 ms 2.43 s ± 115 ms +3.08 %
arco-pro_production-mode_traverse-chunk-modules + exec 3.64 s ± 223 ms 3.66 s ± 210 ms +0.40 %
large-dyn-imports_development-mode + exec 2.11 s ± 32 ms 2.1 s ± 62 ms -0.22 %
large-dyn-imports_production-mode + exec 2.15 s ± 39 ms 2.16 s ± 36 ms +0.33 %
threejs_development-mode_10x + exec 1.53 s ± 29 ms 1.53 s ± 43 ms -0.20 %
threejs_development-mode_10x_hmr + exec 796 ms ± 28 ms 804 ms ± 6.4 ms +1.02 %
threejs_production-mode_10x + exec 5.27 s ± 346 ms 5.2 s ± 86 ms -1.33 %
threejs_production-mode_10x_persistent-cold + exec 5.25 s ± 88 ms 5.27 s ± 46 ms +0.46 %
threejs_production-mode_10x_persistent-hot + exec 4.47 s ± 41 ms 4.54 s ± 277 ms +1.57 %
10000_big_production-mode_disable-minimize + rss memory 8707 MiB ± 70.7 MiB 8752 MiB ± 133 MiB +0.52 %
10000_development-mode + rss memory 656 MiB ± 10.4 MiB 669 MiB ± 26.2 MiB +2.00 %
10000_development-mode_hmr + rss memory 1297 MiB ± 169 MiB 1313 MiB ± 277 MiB +1.17 %
10000_production-mode + rss memory 629 MiB ± 24.1 MiB 658 MiB ± 30.9 MiB +4.56 %
10000_production-mode_persistent-cold + rss memory 742 MiB ± 15 MiB 755 MiB ± 32.6 MiB +1.74 %
10000_production-mode_persistent-hot + rss memory 722 MiB ± 12.2 MiB 743 MiB ± 8.56 MiB +2.98 %
arco-pro_development-mode + rss memory 543 MiB ± 35.2 MiB 593 MiB ± 39.2 MiB +9.15 %
arco-pro_development-mode_hmr + rss memory 649 MiB ± 101 MiB 665 MiB ± 96.4 MiB +2.51 %
arco-pro_production-mode + rss memory 708 MiB ± 23.8 MiB 721 MiB ± 30.8 MiB +1.87 %
arco-pro_production-mode_generate-package-json-webpack-plugin + rss memory 714 MiB ± 23.4 MiB 740 MiB ± 47.6 MiB +3.66 %
arco-pro_production-mode_persistent-cold + rss memory 850 MiB ± 14 MiB 852 MiB ± 36.2 MiB +0.24 %
arco-pro_production-mode_persistent-hot + rss memory 700 MiB ± 24.5 MiB 751 MiB ± 27.2 MiB +7.34 %
arco-pro_production-mode_traverse-chunk-modules + rss memory 709 MiB ± 25.3 MiB 731 MiB ± 47.4 MiB +3.06 %
large-dyn-imports_development-mode + rss memory 640 MiB ± 7.17 MiB 653 MiB ± 6.2 MiB +1.94 %
large-dyn-imports_production-mode + rss memory 522 MiB ± 7.08 MiB 540 MiB ± 5.28 MiB +3.50 %
threejs_development-mode_10x + rss memory 551 MiB ± 14 MiB 557 MiB ± 15.4 MiB +1.06 %
threejs_development-mode_10x_hmr + rss memory 1133 MiB ± 75.4 MiB 1125 MiB ± 84.8 MiB -0.67 %
threejs_production-mode_10x + rss memory 828 MiB ± 28.6 MiB 839 MiB ± 37.2 MiB +1.27 %
threejs_production-mode_10x_persistent-cold + rss memory 956 MiB ± 28.8 MiB 937 MiB ± 43.5 MiB -2.02 %
threejs_production-mode_10x_persistent-hot + rss memory 860 MiB ± 62 MiB 863 MiB ± 46.1 MiB +0.39 %

github-actions[bot] commented on Feb 9, 2025

@github-actions[bot]
Contributor

📝 Ecosystem CI detail: Open

suite result
modernjs ❌ failure
rspress ✅ success
rslib ✅ success
rsbuild ❌ failure
rsdoctor ❌ failure
examples ✅ success
devserver ✅ success
nuxt ✅ success
Please sign in to comment.