From 6594e19f012ceed0e0c4682c5ee832cd4a253008 Mon Sep 17 00:00:00 2001 From: easonysliu Date: Fri, 20 Mar 2026 18:07:58 +0800 Subject: [PATCH] fix: sort ConcatenatedModule export keys for deterministic builds Sort exports_map entries by key before rendering the __webpack_require__.d(exports, {...}) object in ConcatenatedModule. HashMap iteration order is non-deterministic, causing identical source to produce different content hashes across builds. This matches the approach already used in ESMExportInitFragment (init_fragment.rs:356) where export_map is sorted before rendering. Closes #13182 Co-Authored-By: Claude (claude-opus-4-6) --- crates/rspack_core/src/concatenated_module.rs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/crates/rspack_core/src/concatenated_module.rs b/crates/rspack_core/src/concatenated_module.rs index da839add14db..942a7cfa6826 100644 --- a/crates/rspack_core/src/concatenated_module.rs +++ b/crates/rspack_core/src/concatenated_module.rs @@ -1584,14 +1584,21 @@ impl Module for ConcatenatedModule { // Define exports if !exports_map.is_empty() { - let mut definitions = Vec::new(); - for (key, value) in exports_map.iter() { - definitions.push(format!( - "\n {}: {}", - property_name(key).expect("should convert to property_name"), - runtime_template.returning_function(value, "") - )); - } + let mut definitions: Vec<_> = exports_map + .iter() + .map(|(key, value)| { + ( + key.clone(), + format!( + "\n {}: {}", + property_name(key).expect("should convert to property_name"), + runtime_template.returning_function(value, "") + ), + ) + }) + .collect(); + definitions.sort_by(|a, b| a.0.cmp(&b.0)); + let definitions: Vec<_> = definitions.into_iter().map(|(_, s)| s).collect(); let exports_argument = self.get_exports_argument();