From e19ef49f314615ea347eb97e80ad891e85bee50e Mon Sep 17 00:00:00 2001 From: Cong-Cong Pan Date: Thu, 17 Sep 2026 01:02:47 +0800 Subject: [PATCH 1/2] perf: centralize chunk ownership and use array-backed storage --- .agents/ARCHITECTURE.md | 31 ++ Cargo.lock | 10 + crates/rspack_binding_api/src/chunk.rs | 23 +- crates/rspack_binding_api/src/chunk_graph.rs | 7 +- crates/rspack_binding_api/src/chunk_group.rs | 3 +- .../src/compilation/chunks.rs | 18 +- .../rspack_binding_api/src/compilation/mod.rs | 3 +- crates/rspack_binding_api/src/path_data.rs | 2 +- .../artifacts/build_chunk_graph_artifact.rs | 28 +- crates/rspack_core/src/chunk.rs | 16 +- .../src/chunk_graph/chunk_graph_chunk.rs | 485 +++++++++--------- .../src/chunk_graph/chunk_graph_module.rs | 175 +++---- crates/rspack_core/src/chunk_graph/map.rs | 89 ++++ crates/rspack_core/src/chunk_graph/mod.rs | 123 ++++- crates/rspack_core/src/chunk_graph/set.rs | 52 ++ .../rspack_core/src/chunk_graph/slot_map.rs | 152 ++++++ crates/rspack_core/src/chunk_group.rs | 18 +- .../src/compilation/assign_runtime_ids/mod.rs | 12 +- .../build_chunk_graph/code_splitter.rs | 216 ++++---- .../build_chunk_graph/incremental.rs | 88 ++-- .../module_executor/execute.rs | 26 +- .../src/compilation/chunk_ids/mod.rs | 13 +- .../src/compilation/code_generation/mod.rs | 5 +- .../compilation/create_chunk_assets/mod.rs | 15 +- .../src/compilation/create_hash/mod.rs | 55 +- .../compilation/create_module_assets/mod.rs | 3 +- .../compilation/create_module_hashes/mod.rs | 29 +- crates/rspack_core/src/compilation/mod.rs | 45 +- .../compilation/runtime_requirements/mod.rs | 92 ++-- crates/rspack_core/src/compiler/rebuild.rs | 9 +- crates/rspack_core/src/context_module.rs | 3 +- .../rspack_core/src/incremental/mutations.rs | 3 +- crates/rspack_core/src/lib.rs | 80 --- crates/rspack_core/src/runtime_template.rs | 3 +- crates/rspack_core/src/stats/mod.rs | 52 +- crates/rspack_core/src/stats/utils.rs | 10 +- crates/rspack_core/src/ukey.rs | 58 ++- crates/rspack_core_test/Cargo.toml | 15 + crates/rspack_core_test/src/lib.rs | 1 + crates/rspack_core_test/tests/chunk_graph.rs | 206 ++++++++ crates/rspack_core_test/tests/chunk_map.rs | 155 ++++++ crates/rspack_error/src/diagnostic.rs | 5 +- .../src/compact_hashed_chunk_ids_plugin.rs | 25 +- .../src/deterministic_chunk_ids_plugin.rs | 23 +- crates/rspack_ids/src/id_helpers.rs | 52 +- .../rspack_ids/src/named_chunk_ids_plugin.rs | 56 +- .../src/natural_chunk_ids_plugin.rs | 22 +- .../src/occurrence_chunk_ids_plugin.rs | 23 +- crates/rspack_plugin_asset/src/lib.rs | 3 +- crates/rspack_plugin_banner/src/lib.rs | 3 +- .../src/plugin/impl_plugin_for_css_plugin.rs | 13 +- crates/rspack_plugin_css/src/runtime/mod.rs | 6 +- crates/rspack_plugin_css_chunking/src/lib.rs | 24 +- .../src/eval_dev_tool_module_plugin.rs | 3 +- .../src/eval_source_map_dev_tool_plugin.rs | 3 +- .../src/source_map_dev_tool_plugin.rs | 6 +- .../src/lib_manifest_plugin.rs | 7 +- .../src/lib.rs | 3 +- .../src/dependency/dyn_import.rs | 3 +- crates/rspack_plugin_esm_library/src/link.rs | 30 +- .../src/optimize_chunks.rs | 137 ++--- .../rspack_plugin_esm_library/src/plugin.rs | 21 +- .../src/preserve_modules.rs | 41 +- .../rspack_plugin_esm_library/src/render.rs | 12 +- .../src/render/runtime_mode/mod.rs | 3 +- .../rspack_plugin_esm_library/src/runtime.rs | 6 +- .../src/split_chunks.rs | 43 +- .../rspack_plugin_extract_css/src/plugin.rs | 11 +- .../rspack_plugin_extract_css/src/runtime.rs | 11 +- crates/rspack_plugin_hmr/src/lib.rs | 95 ++-- crates/rspack_plugin_html/src/asset.rs | 2 +- .../src/dependency/worker/mod.rs | 3 +- .../src/plugin/impl_plugin_for_js_plugin.rs | 17 +- .../src/plugin/mod.rs | 20 +- .../src/plugin/module_concatenation_plugin.rs | 25 +- .../src/plugin/runtime_context.rs | 14 +- .../src/plugin/url_plugin.rs | 6 +- .../rspack_plugin_javascript/src/runtime.rs | 6 +- .../src/runtime_context.rs | 6 +- .../src/amd_library_plugin.rs | 3 +- .../src/assign_library_plugin.rs | 9 +- .../src/jsonp_library_plugin.rs | 3 +- .../src/module_library_plugin.rs | 3 +- .../src/system_library_plugin.rs | 3 +- .../src/umd_library_plugin.rs | 3 +- crates/rspack_plugin_library/src/utils.rs | 3 +- .../src/chunk_combination.rs | 25 +- .../src/lib.rs | 89 ++-- .../src/lib.rs | 36 +- .../src/container/container_plugin.rs | 3 +- .../embed_federation_runtime_module.rs | 6 +- .../embed_federation_runtime_plugin.rs | 9 +- .../src/container/expose_runtime_module.rs | 6 +- .../federation_data_runtime_module.rs | 3 +- .../hoist_container_references_plugin.rs | 33 +- .../src/container/remote_runtime_module.rs | 6 +- crates/rspack_plugin_mf/src/manifest/asset.rs | 11 +- crates/rspack_plugin_mf/src/manifest/mod.rs | 15 +- crates/rspack_plugin_mf/src/manifest/utils.rs | 9 +- .../sharing/consume_shared_runtime_module.rs | 9 +- .../src/sharing/share_runtime_module.rs | 6 +- .../src/sharing/shared_container_plugin.rs | 3 +- .../src/lib.rs | 3 +- crates/rspack_plugin_progress/src/lib.rs | 28 +- .../src/lib.rs | 43 +- .../src/lib.rs | 15 +- crates/rspack_plugin_rsc/src/client_plugin.rs | 10 +- .../src/manifest_runtime_module.rs | 3 +- crates/rspack_plugin_rsc/src/server_plugin.rs | 20 +- .../rspack_plugin_rsdoctor/src/chunk_graph.rs | 12 +- crates/rspack_plugin_rsdoctor/src/plugin.rs | 6 +- crates/rspack_plugin_rstest/src/plugin.rs | 11 +- .../src/array_push_callback_chunk_format.rs | 9 +- .../src/chunk_prefetch_preload.rs | 6 +- .../src/common_js_chunk_format.rs | 12 +- crates/rspack_plugin_runtime/src/helpers.rs | 12 +- .../src/module_chunk_format.rs | 22 +- .../src/module_chunk_loading.rs | 3 +- .../src/runtime_module/auto_public_path.rs | 3 +- .../src/runtime_module/base_uri.rs | 3 +- .../src/runtime_module/chunk_name.rs | 3 +- .../runtime_module/chunk_prefetch_startup.rs | 4 +- .../src/runtime_module/get_chunk_filename.rs | 7 +- .../get_chunk_update_filename.rs | 3 +- .../src/runtime_module/get_main_filename.rs | 3 +- .../import_scripts_chunk_loading.rs | 3 +- .../src/runtime_module/jsonp_chunk_loading.rs | 3 +- .../runtime_module/module_chunk_loading.rs | 3 +- .../runtime_module/readfile_chunk_loading.rs | 3 +- .../require_js_chunk_loading.rs | 3 +- .../src/runtime_module/runtime_id.rs | 3 +- .../startup_chunk_dependencies.rs | 4 +- .../src/runtime_module/utils.rs | 12 +- .../src/runtime_plugin.rs | 9 +- crates/rspack_plugin_size_limits/src/lib.rs | 8 +- .../rspack_plugin_split_chunks/src/common.rs | 9 +- .../src/plugin/chunk.rs | 41 +- .../src/plugin/max_request.rs | 2 +- .../src/plugin/max_size.rs | 49 +- .../src/plugin/mod.rs | 37 +- .../src/plugin/module_group.rs | 29 +- crates/rspack_plugin_sri/src/asset.rs | 14 +- crates/rspack_plugin_sri/src/runtime.rs | 18 +- crates/rspack_plugin_sri/src/util.rs | 3 +- .../rspack_plugin_wasm/src/loading_plugin.rs | 3 +- crates/rspack_plugin_wasm/src/wasm_plugin.rs | 3 +- .../en/api/javascript-api/architecture.mdx | 2 + .../zh/api/javascript-api/architecture.mdx | 2 + .../benches/groups/build_chunk_graph.rs | 4 +- .../benches/groups/compilation_stages.rs | 160 +++--- 150 files changed, 2543 insertions(+), 1639 deletions(-) create mode 100644 crates/rspack_core/src/chunk_graph/map.rs create mode 100644 crates/rspack_core/src/chunk_graph/set.rs create mode 100644 crates/rspack_core/src/chunk_graph/slot_map.rs create mode 100644 crates/rspack_core_test/Cargo.toml create mode 100644 crates/rspack_core_test/src/lib.rs create mode 100644 crates/rspack_core_test/tests/chunk_graph.rs create mode 100644 crates/rspack_core_test/tests/chunk_map.rs diff --git a/.agents/ARCHITECTURE.md b/.agents/ARCHITECTURE.md index ba32c969effc..c9c2d21d9998 100644 --- a/.agents/ARCHITECTURE.md +++ b/.agents/ARCHITECTURE.md @@ -102,6 +102,37 @@ Manages modules and their relationships through dependencies and connections. ### Chunk system +`ChunkGraph` owns both Chunk values and their relationships. Allocate through +`ChunkGraph::create_chunk`; named creation and removal go through +`BuildChunkGraphArtifact`. `ChunkSlotMap` exposes value access, not independent +insertion or removal. `CompilationChunkIds` receives `&mut Compilation`. + +A `ChunkUkey` combines an array slot with a process-wide allocation identity. +Deleted slots may be reused, but every access checks the full key. Graph snapshots +copy values and the graph's free-slot stack without copying the identity allocator. +Identities are `NonZeroU32`. Following the layout used by `slotmap::SecondaryMap`, +each table stores `Vec>`: the identity supplies a niche for +the empty state, without manual initialization or destruction. Only `ChunkGraph` +allocates identities and recycles slots; value tables carry no allocation state. +Sequential insertion uses `Vec::push`, and `with_capacity` reserves storage without +initializing slots. Traversal still scans the slot high-water mark, so sparse or +historical collections should retain HashMap/HashSet storage. +`ChunkMap` reuses that storage for per-Chunk working data, such as CodeSplitter's +module masks. Its insertion API requires the current owning Chunk table and checks +membership before replacing a slot. Reserve using `slot_count()` (plus expected +new Chunks while building), not `len()`. A map can hold one identity per slot and +must not replace caches that need multiple historical identities. +`ChunkSet` uses the same storage for current-graph membership, retaining the full +identity instead of only a slot bit. Per-pass tables for split-chunk indices, +chunk-combination lookup, and chunk runtime requirements reserve the slot +high-water mark up front. Algorithms that expose processing order sort by their +semantic order or the full key; they must not derive order from reusable slots. +The `as_u32()` +projection is for tooling identities, never array indexing; `as_u64()` preserves +both fields for process-local diagnostics. These handles are not persistent cache +identities. Graph algorithms access their owned Chunk table directly; topology +and values can be borrowed separately when an algorithm needs both. + Chunks are groups of modules bundled together. **Chunk Types:** diff --git a/Cargo.lock b/Cargo.lock index 6bf2eefed8bc..e2168b46625a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4278,6 +4278,16 @@ dependencies = [ "winnow", ] +[[package]] +name = "rspack_core_test" +version = "0.102.5" +dependencies = [ + "rspack_cacheable", + "rspack_collections", + "rspack_core", + "rspack_error", +] + [[package]] name = "rspack_dojang" version = "0.102.5" diff --git a/crates/rspack_binding_api/src/chunk.rs b/crates/rspack_binding_api/src/chunk.rs index 4c2006949aab..fc3c94398d33 100644 --- a/crates/rspack_binding_api/src/chunk.rs +++ b/crates/rspack_binding_api/src/chunk.rs @@ -23,6 +23,27 @@ pub struct Chunk { } impl Chunk { + // Incremental snapshots preserve keys across compilations. Native identity + // does not extend the lifetime of a JavaScript handle from an earlier build. + pub(crate) fn belongs_to(&self, compilation: &Compilation) -> bool { + self.compilation_id == compilation.id() + && compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .contains(&self.chunk_ukey) + } + + pub(crate) fn validate_compilation(&self, compilation: &Compilation) -> napi::Result<()> { + if self.belongs_to(compilation) { + Ok(()) + } else { + Err(napi::Error::from_reason( + "Chunk does not belong to this Compilation or has been removed", + )) + } + } + fn with_compilation( &self, f: impl FnOnce(&Compilation) -> napi::Result, @@ -37,7 +58,7 @@ impl Chunk { self.with_compilation(|compilation| { if let Some(chunk) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph.chunks .get(&self.chunk_ukey) { f(compilation, chunk) diff --git a/crates/rspack_binding_api/src/chunk_graph.rs b/crates/rspack_binding_api/src/chunk_graph.rs index 3bd9e4b2875b..2cdb9ace6e8d 100644 --- a/crates/rspack_binding_api/src/chunk_graph.rs +++ b/crates/rspack_binding_api/src/chunk_graph.rs @@ -46,6 +46,7 @@ impl ChunkGraph { #[napi(ts_return_type = "boolean")] pub fn has_chunk_entry_dependent_chunks(&self, chunk: &Chunk) -> Result { self.with_compilation(|compilation| { + chunk.validate_compilation(compilation)?; Ok( compilation .build_chunk_graph_artifact @@ -61,6 +62,7 @@ impl ChunkGraph { #[napi(ts_return_type = "Module[]")] pub fn get_chunk_modules(&self, chunk: &Chunk) -> Result> { self.with_compilation(|compilation| { + chunk.validate_compilation(compilation)?; let module_graph = compilation.get_module_graph(); let modules = compilation .build_chunk_graph_artifact @@ -84,6 +86,7 @@ impl ChunkGraph { #[napi(ts_return_type = "Iterable")] pub fn get_chunk_entry_modules_iterable(&self, chunk: &Chunk) -> Result> { self.with_compilation(|compilation| { + chunk.validate_compilation(compilation)?; let modules = compilation .build_chunk_graph_artifact .chunk_graph @@ -102,6 +105,7 @@ impl ChunkGraph { #[napi(ts_return_type = "number")] pub fn get_number_of_entry_modules(&self, chunk: &Chunk) -> Result { self.with_compilation(|compilation| { + chunk.validate_compilation(compilation)?; Ok( compilation .build_chunk_graph_artifact @@ -117,12 +121,12 @@ impl ChunkGraph { chunk: &Chunk, ) -> Result> { self.with_compilation(|compilation| { + chunk.validate_compilation(compilation)?; let chunks = compilation .build_chunk_graph_artifact .chunk_graph .get_chunk_entry_dependent_chunks_iterable( &chunk.chunk_ukey, - &compilation.build_chunk_graph_artifact.chunk_by_ukey, &compilation.build_chunk_graph_artifact.chunk_group_by_ukey, ); @@ -142,6 +146,7 @@ impl ChunkGraph { source_type: String, ) -> Result> { self.with_compilation(|compilation| { + chunk.validate_compilation(compilation)?; let module_graph = compilation.get_module_graph(); let chunk_modules = compilation diff --git a/crates/rspack_binding_api/src/chunk_group.rs b/crates/rspack_binding_api/src/chunk_group.rs index 9c4e2dd59172..fc93c031a31e 100644 --- a/crates/rspack_binding_api/src/chunk_group.rs +++ b/crates/rspack_binding_api/src/chunk_group.rs @@ -169,7 +169,8 @@ impl ChunkGroup { .filter_map(|chunk_ukey| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_ukey) .map(|chunk| chunk.files().iter()) }) diff --git a/crates/rspack_binding_api/src/compilation/chunks.rs b/crates/rspack_binding_api/src/compilation/chunks.rs index 606cbdfb6a56..1c2ec1a84218 100644 --- a/crates/rspack_binding_api/src/compilation/chunks.rs +++ b/crates/rspack_binding_api/src/compilation/chunks.rs @@ -40,7 +40,13 @@ impl Chunks { #[napi(getter)] pub fn size(&self) -> napi::Result { let compilation = self.as_ref()?; - Ok(compilation.build_chunk_graph_artifact.chunk_by_ukey.len() as u32) + Ok( + compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .len() as u32, + ) } #[napi(js_name = "_values", ts_return_type = "Chunk[]")] @@ -49,7 +55,8 @@ impl Chunks { Ok( compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .keys() .map(|chunk_ukey| ChunkWrapper::new(*chunk_ukey, compilation)) .collect::>(), @@ -59,11 +66,6 @@ impl Chunks { #[napi(js_name = "_has")] pub fn has(&self, chunk: &Chunk) -> napi::Result { let compilation = self.as_ref()?; - Ok( - compilation - .build_chunk_graph_artifact - .chunk_by_ukey - .contains(&chunk.chunk_ukey), - ) + Ok(chunk.belongs_to(compilation)) } } diff --git a/crates/rspack_binding_api/src/compilation/mod.rs b/crates/rspack_binding_api/src/compilation/mod.rs index 5ad5ed777c10..44e5b1893d65 100644 --- a/crates/rspack_binding_api/src/compilation/mod.rs +++ b/crates/rspack_binding_api/src/compilation/mod.rs @@ -304,7 +304,8 @@ impl JsCompilation { .and_then(|c| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(c) .map(|chunk| ChunkWrapper::new(chunk.ukey(), compilation)) }), diff --git a/crates/rspack_binding_api/src/path_data.rs b/crates/rspack_binding_api/src/path_data.rs index 187458a1c7a9..51549c384abf 100644 --- a/crates/rspack_binding_api/src/path_data.rs +++ b/crates/rspack_binding_api/src/path_data.rs @@ -105,7 +105,7 @@ impl JsPathData { Some( compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph.chunks .get(&chunk_ukey) .ok_or_else(|| { napi::Error::from_reason(format!( diff --git a/crates/rspack_core/src/artifacts/build_chunk_graph_artifact.rs b/crates/rspack_core/src/artifacts/build_chunk_graph_artifact.rs index eaf3d9c0e26b..22f32e60c460 100644 --- a/crates/rspack_core/src/artifacts/build_chunk_graph_artifact.rs +++ b/crates/rspack_core/src/artifacts/build_chunk_graph_artifact.rs @@ -8,8 +8,7 @@ use rustc_hash::FxHashMap as HashMap; use tracing::instrument; use crate::{ - ArtifactExt, ChunkByUkey, ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey, ChunkUkey, Compilation, - Logger, + ArtifactExt, ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey, ChunkUkey, Compilation, Logger, build_chunk_graph::code_splitter::CodeSplitter, fast_set, incremental::{IncrementalPasses, Mutation}, @@ -17,7 +16,6 @@ use crate::{ #[derive(Debug, Default)] pub struct BuildChunkGraphArtifact { - pub chunk_by_ukey: ChunkByUkey, pub chunk_graph: ChunkGraph, pub chunk_group_by_ukey: ChunkGroupByUkey, pub entrypoints: FxIndexMap, @@ -29,6 +27,26 @@ pub struct BuildChunkGraphArtifact { } impl BuildChunkGraphArtifact { + pub fn add_named_chunk(&mut self, name: String) -> rspack_error::Result<(ChunkUkey, bool)> { + if let Some(key) = self.named_chunks.get(&name).copied() { + assert!(self.chunk_graph.chunks.contains(&key)); + return Ok((key, false)); + } + let key = self + .chunk_graph + .create_chunk(Some(name.clone()), crate::ChunkKind::Normal)?; + self.named_chunks.insert(name, key); + Ok((key, true)) + } + + pub fn remove_chunk(&mut self, key: &ChunkUkey) -> Option { + let chunk = self + .chunk_graph + .remove_chunk(key, &mut self.chunk_group_by_ukey)?; + self.named_chunks.retain(|_, stored| stored != key); + Some(chunk) + } + pub(crate) fn set_code_splitter(&mut self, code_splitter: CodeSplitter) { fast_set(&mut self.code_splitter, code_splitter); } @@ -104,13 +122,12 @@ impl BuildChunkGraphArtifact { /// cached chunks across incremental compilations, so we need to restore the /// same state before running the next sealing/rendering pipeline. fn reset_chunk_rendered_state(&mut self) { - for chunk in self.chunk_by_ukey.values_mut() { + for chunk in self.chunk_graph.chunks.values_mut() { chunk.set_rendered(false); } } fn reset_for_rebuild(&mut self) { - self.chunk_by_ukey = Default::default(); self.chunk_graph = Default::default(); self.chunk_group_by_ukey = Default::default(); self.entrypoints.clear(); @@ -188,7 +205,6 @@ impl ArtifactExt for BuildChunkGraphArtifact { fn recover(_incremental: &crate::incremental::Incremental, new: &mut Self, old: &mut Self) { new.code_splitter = mem::take(&mut old.code_splitter); rayon::scope(|s| { - s.spawn(|_| new.chunk_by_ukey.clone_from(&old.chunk_by_ukey)); s.spawn(|_| new.chunk_graph.clone_from(&old.chunk_graph)); s.spawn(|_| new.chunk_group_by_ukey.clone_from(&old.chunk_group_by_ukey)); diff --git a/crates/rspack_core/src/chunk.rs b/crates/rspack_core/src/chunk.rs index f011bb6192e9..1264ea4baa99 100644 --- a/crates/rspack_core/src/chunk.rs +++ b/crates/rspack_core/src/chunk.rs @@ -310,12 +310,12 @@ impl Chunk { } impl Chunk { - pub fn new(name: Option, kind: ChunkKind) -> Self { + pub(crate) fn new(ukey: ChunkUkey, name: Option, kind: ChunkKind) -> Self { Self { name, filename_template: None, css_filename_template: None, - ukey: ChunkUkey::new(), + ukey, id: None, id_name_hints: Default::default(), prevent_integration: false, @@ -378,12 +378,6 @@ impl Chunk { split_data.add_runtime(&self.runtime); } - pub fn split(&mut self, new_chunk: &mut Chunk, chunk_group_by_ukey: &mut ChunkGroupByUkey) { - let mut split_data = ChunkSplitData::with_capacity(self.groups.len(), self.id_name_hints.len()); - self.split_collect_new_chunk_data(new_chunk.ukey, chunk_group_by_ukey, &mut split_data); - split_data.apply_to(new_chunk); - } - pub fn can_be_initial(&self, chunk_group_by_ukey: &ChunkGroupByUkey) -> bool { self .groups @@ -921,7 +915,8 @@ impl Chunk { if filter_fn(chunk_ukey, compilation) && let Some(chunk_id) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey) .id() .cloned() @@ -952,7 +947,8 @@ impl Chunk { ) -> Option<(ChunkId, Vec)> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if let (Some(chunk_id), Some(child_chunk_ids)) = ( chunk.id().cloned(), diff --git a/crates/rspack_core/src/chunk_graph/chunk_graph_chunk.rs b/crates/rspack_core/src/chunk_graph/chunk_graph_chunk.rs index b6843c4b7669..d6f4f6bf038d 100644 --- a/crates/rspack_core/src/chunk_graph/chunk_graph_chunk.rs +++ b/crates/rspack_core/src/chunk_graph/chunk_graph_chunk.rs @@ -14,7 +14,7 @@ use serde::{Serialize, Serializer}; use ustr::Ustr; use crate::{ - Chunk, ChunkByUkey, ChunkGraph, ChunkGraphModule, ChunkGroupByUkey, ChunkGroupUkey, ChunkUkey, + Chunk, ChunkGraph, ChunkGraphModule, ChunkGroupByUkey, ChunkGroupUkey, ChunkSlotMap, ChunkUkey, Compilation, ExportsInfoArtifact, Module, ModuleGraph, ModuleGraphCacheArtifact, ModuleIdentifier, ModuleRef, RuntimeGlobals, RuntimeModule, SideEffectsStateArtifact, SourceType, find_graph_roots, merge_runtime, @@ -113,18 +113,7 @@ fn get_modules_size(modules: &[&ModuleRef], compilation: &Compilation) -> f64 { size } -impl ChunkGraph { - pub fn add_chunk(&mut self, chunk_ukey: ChunkUkey) { - self - .chunk_graph_chunk_by_chunk_ukey - .entry(chunk_ukey) - .or_default(); - } - - pub fn remove_chunk(&mut self, chunk_ukey: &ChunkUkey) -> Option { - self.chunk_graph_chunk_by_chunk_ukey.remove(chunk_ukey) - } - +impl super::ChunkGraphTopology { pub fn replace_module( &mut self, old_module_id: &ModuleIdentifier, @@ -248,8 +237,8 @@ impl ChunkGraph { let chunk_graph_chunk = self .chunk_graph_chunk_by_chunk_ukey - .entry(chunk) - .or_default(); + .get_mut(&chunk) + .expect("Chunk must be created before connecting it"); chunk_graph_chunk .entry_modules .insert(module_identifier, entrypoint); @@ -285,8 +274,8 @@ impl ChunkGraph { let chunk_graph_chunk = self .chunk_graph_chunk_by_chunk_ukey - .entry(chunk) - .or_default(); + .get_mut(&chunk) + .expect("Chunk must be created before connecting it"); chunk_graph_chunk.modules.insert(module_identifier); } @@ -304,8 +293,8 @@ impl ChunkGraph { self .chunk_graph_chunk_by_chunk_ukey - .entry(chunk) - .or_default(); + .get_mut(&chunk) + .expect("Chunk must be created before connecting it"); let cgc = self.expect_chunk_graph_chunk_mut(chunk); if !cgc.runtime_modules.contains(&module_identifier) { cgc.runtime_modules.push(module_identifier); @@ -562,51 +551,6 @@ impl ChunkGraph { false } - pub fn set_chunk_runtime_requirements( - compilation: &mut Compilation, - chunk_ukey: ChunkUkey, - runtime_requirements: RuntimeGlobals, - ) { - compilation - .cgc_runtime_requirements_artifact - .insert(chunk_ukey, runtime_requirements); - } - - pub fn set_tree_runtime_requirements( - compilation: &mut Compilation, - chunk_ukey: ChunkUkey, - runtime_requirements: RuntimeGlobals, - ) { - Self::set_chunk_runtime_requirements(compilation, chunk_ukey, runtime_requirements); - } - - pub fn get_chunk_runtime_requirements<'a>( - compilation: &'a Compilation, - chunk_ukey: &ChunkUkey, - ) -> &'a RuntimeGlobals { - compilation - .cgc_runtime_requirements_artifact - .get(chunk_ukey) - .unwrap_or_else(|| { - let chunk = compilation - .build_chunk_graph_artifact - .chunk_by_ukey - .expect_get(chunk_ukey); - let cgc = compilation - .build_chunk_graph_artifact - .chunk_graph - .expect_chunk_graph_chunk(chunk_ukey); - panic!("Should have runtime requirements for chunk:\n{chunk:#?}\n{cgc:#?}") - }) - } - - pub fn get_tree_runtime_requirements<'a>( - compilation: &'a Compilation, - chunk_ukey: &ChunkUkey, - ) -> &'a RuntimeGlobals { - Self::get_chunk_runtime_requirements(compilation, chunk_ukey) - } - pub fn get_chunk_runtime_modules_in_order<'a>( &self, chunk_ukey: &ChunkUkey, @@ -657,7 +601,8 @@ impl ChunkGraph { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); for c in chunk .get_all_referenced_chunks(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) @@ -665,7 +610,8 @@ impl ChunkGraph { { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(c); map.insert(chunk.expect_id().to_string(), filter(c, compilation)); } @@ -737,21 +683,6 @@ impl ChunkGraph { modules } - pub fn disconnect_chunk( - &mut self, - chunk: &mut Chunk, - chunk_group_by_ukey: &mut ChunkGroupByUkey, - ) { - let chunk_ukey = &chunk.ukey(); - let cgc = self.expect_chunk_graph_chunk_mut(*chunk_ukey); - let cgc_modules = std::mem::take(&mut cgc.modules); - for module in cgc_modules { - let cgm = self.expect_chunk_graph_module_mut(module); - cgm.chunks.remove(chunk_ukey); - } - chunk.disconnect_from_groups(chunk_group_by_ukey) - } - pub fn has_chunk_entry_dependent_chunks( &self, chunk_ukey: &ChunkUkey, @@ -769,12 +700,224 @@ impl ChunkGraph { false } + pub fn disconnect_chunk_and_entry_module( + &mut self, + chunk: &ChunkUkey, + module_identifier: ModuleIdentifier, + ) { + let chunk_graph_module = self.expect_chunk_graph_module_mut(module_identifier); + chunk_graph_module.entry_in_chunks.remove(chunk); + + let chunk_graph_chunk = self.expect_chunk_graph_chunk_mut(*chunk); + chunk_graph_chunk.entry_modules.remove(&module_identifier); + + if let Some(source_types_by_module) = &mut chunk_graph_chunk.source_types_by_module { + source_types_by_module.remove(&module_identifier); + } + } + + pub(super) fn integrate_chunks( + &mut self, + a: &ChunkUkey, + b: &ChunkUkey, + chunk_by_ukey: &mut ChunkSlotMap, + chunk_group_by_ukey: &mut ChunkGroupByUkey, + module_graph: &ModuleGraph, + ) { + let chunk_b = chunk_by_ukey.expect_get(b).clone(); + let chunk_a = chunk_by_ukey.expect_get_mut(a); + + // Decide for one name (deterministic) + if let (Some(chunk_a_name), Some(chunk_b_name)) = (chunk_a.name(), chunk_b.name()) { + if (self.get_number_of_entry_modules(a) > 0) == (self.get_number_of_entry_modules(b) > 0) { + // When both chunks have entry modules or none have one, use + // shortest name + let new_name = if chunk_a_name.len() != chunk_b_name.len() { + if chunk_a_name.len() < chunk_b_name.len() { + chunk_a_name + } else { + chunk_b_name + } + } else if chunk_a_name < chunk_b_name { + chunk_a_name + } else { + chunk_b_name + }; + chunk_a.set_name(Some(new_name.to_string())); + } else if self.get_number_of_entry_modules(b) > 0 { + // Pick the name of the chunk with the entry module + chunk_a.set_name(chunk_b.name().map(ToOwned::to_owned)); + } + } else if let Some(chunk_b_name) = chunk_b.name() { + chunk_a.set_name(Some(chunk_b_name.to_string())); + } + + // Merge id name hints + for hint in chunk_b.id_name_hints() { + chunk_a.add_id_name_hints(hint.clone()); + } + + // Merge runtime + chunk_a.set_runtime(merge_runtime(chunk_a.runtime(), chunk_b.runtime())); + + // get_chunk_modules is used here to create a clone, because disconnect_chunk_and_module modifies + for module in self.get_chunk_modules(b, module_graph) { + self.disconnect_chunk_and_module(b, module.identifier()); + self.connect_chunk_and_module(*a, module.identifier()); + } + + let chunk_entry_modules_with_chunk_group_iterable = self + .get_chunk_entry_modules_with_chunk_group_iterable(b) + .clone(); + for (module, chunk_group) in chunk_entry_modules_with_chunk_group_iterable { + self.disconnect_chunk_and_entry_module(b, module); + self.connect_chunk_and_entry_module(*a, module, chunk_group); + } + + let mut remove_group_ukeys = vec![]; + for &chunk_group_ukey in chunk_b.groups() { + let chunk_group = chunk_group_by_ukey.expect_get_mut(&chunk_group_ukey); + chunk_group.replace_chunk(b, a); + chunk_a.add_group(chunk_group_ukey); + remove_group_ukeys.push(chunk_group_ukey); + } + + let chunk_b = chunk_by_ukey.expect_get_mut(b); + for group_ukey in remove_group_ukeys { + chunk_b.remove_group(&group_ukey); + } + } + + pub fn set_runtime_id(&mut self, runtime: String, id: Option) { + self.runtime_ids.insert(runtime, id); + } + + pub fn get_runtime_id(&self, runtime: &str) -> Option { + self.runtime_ids.get(runtime).and_then(|v| v.to_owned()) + } + + pub fn set_chunk_modules_source_types( + &mut self, + chunk: &ChunkUkey, + module: ModuleIdentifier, + source_types: FxHashSet, + ) { + let cgc = self + .chunk_graph_chunk_by_chunk_ukey + .get_mut(chunk) + .expect("should have cgc"); + + if let Some(source_types_by_module) = &mut cgc.source_types_by_module { + source_types_by_module.insert(module, source_types); + } else { + let mut map = IdentifierMap::default(); + map.insert(module, source_types); + cgc.source_types_by_module = Some(map); + } + } + + pub fn get_chunk_module_source_types( + &self, + chunk: &ChunkUkey, + module: &ModuleRef, + module_graph: &ModuleGraph, + ) -> FxHashSet { + self + .chunk_graph_chunk_by_chunk_ukey + .get(chunk) + .and_then(|cgc| { + if let Some(source_types_by_module) = &cgc.source_types_by_module { + return source_types_by_module.get(&module.identifier()).cloned(); + } + + None + }) + .unwrap_or_else(|| module.source_types(module_graph).iter().copied().collect()) + } +} + +#[cfg(test)] +mod tests { + use super::ChunkId; + + #[test] + fn chunk_id_serialize_matches_runtime_numeric_rules() { + assert_eq!(simd_json::to_string(&ChunkId::from("903")).unwrap(), "903"); + assert_eq!( + simd_json::to_string(&ChunkId::from("01")).unwrap(), + "\"01\"" + ); + assert_eq!( + simd_json::to_string(&ChunkId::from("main")).unwrap(), + "\"main\"" + ); + assert_eq!( + simd_json::to_string(&ChunkId::from("4294967295")).unwrap(), + "4294967295" + ); + assert_eq!( + simd_json::to_string(&ChunkId::from("4294967296")).unwrap(), + "\"4294967296\"" + ); + assert_eq!( + simd_json::to_string(&vec![ChunkId::from("01"), ChunkId::from("903")]).unwrap(), + "[\"01\",903]" + ); + } +} + +impl ChunkGraph { + pub fn set_chunk_runtime_requirements( + compilation: &mut Compilation, + chunk_ukey: ChunkUkey, + runtime_requirements: RuntimeGlobals, + ) { + compilation + .cgc_runtime_requirements_artifact + .insert(chunk_ukey, runtime_requirements); + } + pub fn set_tree_runtime_requirements( + compilation: &mut Compilation, + chunk_ukey: ChunkUkey, + runtime_requirements: RuntimeGlobals, + ) { + Self::set_chunk_runtime_requirements(compilation, chunk_ukey, runtime_requirements); + } + pub fn get_chunk_runtime_requirements<'a>( + compilation: &'a Compilation, + chunk_ukey: &ChunkUkey, + ) -> &'a RuntimeGlobals { + compilation + .cgc_runtime_requirements_artifact + .get(chunk_ukey) + .unwrap_or_else(|| { + let chunk = compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .expect_get(chunk_ukey); + let cgc = compilation + .build_chunk_graph_artifact + .chunk_graph + .expect_chunk_graph_chunk(chunk_ukey); + panic!("Should have runtime requirements for chunk:\n{chunk:#?}\n{cgc:#?}") + }) + } + pub fn get_tree_runtime_requirements<'a>( + compilation: &'a Compilation, + chunk_ukey: &ChunkUkey, + ) -> &'a RuntimeGlobals { + Self::get_chunk_runtime_requirements(compilation, chunk_ukey) + } +} + +impl ChunkGraph { pub fn get_runtime_chunk_dependent_chunks_iterable( &self, chunk_ukey: &ChunkUkey, - chunk_by_ukey: &ChunkByUkey, chunk_group_by_ukey: &ChunkGroupByUkey, ) -> impl Iterator { + let chunk_by_ukey = &self.chunks; let mut set = FxIndexSet::default(); let mut entrypoints = FxIndexSet::default(); @@ -829,13 +972,12 @@ impl ChunkGraph { set.into_iter() } - pub fn get_chunk_entry_dependent_chunks_iterable( &self, chunk_ukey: &ChunkUkey, - chunk_by_ukey: &ChunkByUkey, chunk_group_by_ukey: &ChunkGroupByUkey, ) -> impl Iterator { + let chunk_by_ukey = &self.chunks; let chunk = chunk_by_ukey.expect_get(chunk_ukey); let mut set = FxIndexSet::default(); for chunk_group_ukey in chunk.get_sorted_groups_iter(chunk_group_by_ukey) { @@ -857,30 +999,13 @@ impl ChunkGraph { } set.into_iter() } - - pub fn disconnect_chunk_and_entry_module( - &mut self, - chunk: &ChunkUkey, - module_identifier: ModuleIdentifier, - ) { - let chunk_graph_module = self.expect_chunk_graph_module_mut(module_identifier); - chunk_graph_module.chunks.remove(chunk); - - let chunk_graph_chunk = self.expect_chunk_graph_chunk_mut(*chunk); - chunk_graph_chunk.entry_modules.remove(&module_identifier); - - if let Some(source_types_by_module) = &mut chunk_graph_chunk.source_types_by_module { - source_types_by_module.remove(&module_identifier); - } - } - pub fn can_chunks_be_integrated( &self, chunk_a_ukey: &ChunkUkey, chunk_b_ukey: &ChunkUkey, - chunk_by_ukey: &ChunkByUkey, chunk_group_by_ukey: &ChunkGroupByUkey, ) -> bool { + let chunk_by_ukey = &self.chunks; let chunk_a = chunk_by_ukey.expect_get(chunk_a_ukey); let chunk_b = chunk_by_ukey.expect_get(chunk_b_ukey); if chunk_a.prevent_integration() || chunk_b.prevent_integration() { @@ -934,16 +1059,15 @@ impl ChunkGraph { true } - pub fn get_chunk_size( &self, chunk_ukey: &ChunkUkey, options: &ChunkSizeOptions, - chunk_by_ukey: &ChunkByUkey, chunk_group_by_ukey: &ChunkGroupByUkey, module_graph: &ModuleGraph, compilation: &Compilation, ) -> f64 { + let chunk_by_ukey = &self.chunks; let cgc = self.expect_chunk_graph_chunk(chunk_ukey); let modules: Vec<&ModuleRef> = cgc .modules @@ -962,18 +1086,16 @@ impl ChunkGraph { 1f64 }) } - - #[allow(clippy::too_many_arguments)] pub fn get_integrated_chunks_size( &self, chunk_a_ukey: &ChunkUkey, chunk_b_ukey: &ChunkUkey, options: &ChunkSizeOptions, - chunk_by_ukey: &ChunkByUkey, chunk_group_by_ukey: &ChunkGroupByUkey, module_graph: &ModuleGraph, compilation: &Compilation, ) -> f64 { + let chunk_by_ukey = &self.chunks; let cgc_a = self.expect_chunk_graph_chunk(chunk_a_ukey); let cgc_b = self.expect_chunk_graph_chunk(chunk_b_ukey); let mut all_modules: Vec<&ModuleRef> = cgc_a @@ -1004,153 +1126,4 @@ impl ChunkGraph { 1f64 }) } - - pub fn integrate_chunks( - &mut self, - a: &ChunkUkey, - b: &ChunkUkey, - chunk_by_ukey: &mut ChunkByUkey, - chunk_group_by_ukey: &mut ChunkGroupByUkey, - module_graph: &ModuleGraph, - ) { - let chunk_b = chunk_by_ukey.expect_get(b).clone(); - let chunk_a = chunk_by_ukey.expect_get_mut(a); - - // Decide for one name (deterministic) - if let (Some(chunk_a_name), Some(chunk_b_name)) = (chunk_a.name(), chunk_b.name()) { - if (self.get_number_of_entry_modules(a) > 0) == (self.get_number_of_entry_modules(b) > 0) { - // When both chunks have entry modules or none have one, use - // shortest name - let new_name = if chunk_a_name.len() != chunk_b_name.len() { - if chunk_a_name.len() < chunk_b_name.len() { - chunk_a_name - } else { - chunk_b_name - } - } else if chunk_a_name < chunk_b_name { - chunk_a_name - } else { - chunk_b_name - }; - chunk_a.set_name(Some(new_name.to_string())); - } else if self.get_number_of_entry_modules(b) > 0 { - // Pick the name of the chunk with the entry module - chunk_a.set_name(chunk_b.name().map(ToOwned::to_owned)); - } - } else if let Some(chunk_b_name) = chunk_b.name() { - chunk_a.set_name(Some(chunk_b_name.to_string())); - } - - // Merge id name hints - for hint in chunk_b.id_name_hints() { - chunk_a.add_id_name_hints(hint.clone()); - } - - // Merge runtime - chunk_a.set_runtime(merge_runtime(chunk_a.runtime(), chunk_b.runtime())); - - // get_chunk_modules is used here to create a clone, because disconnect_chunk_and_module modifies - for module in self.get_chunk_modules(b, module_graph) { - self.disconnect_chunk_and_module(b, module.identifier()); - self.connect_chunk_and_module(*a, module.identifier()); - } - - let chunk_entry_modules_with_chunk_group_iterable = self - .get_chunk_entry_modules_with_chunk_group_iterable(b) - .clone(); - for (module, chunk_group) in chunk_entry_modules_with_chunk_group_iterable { - self.disconnect_chunk_and_entry_module(b, module); - self.connect_chunk_and_entry_module(*a, module, chunk_group); - } - - let mut remove_group_ukeys = vec![]; - for &chunk_group_ukey in chunk_b.groups() { - let chunk_group = chunk_group_by_ukey.expect_get_mut(&chunk_group_ukey); - chunk_group.replace_chunk(b, a); - chunk_a.add_group(chunk_group_ukey); - remove_group_ukeys.push(chunk_group_ukey); - } - - let chunk_b = chunk_by_ukey.expect_get_mut(b); - for group_ukey in remove_group_ukeys { - chunk_b.remove_group(&group_ukey); - } - } - - pub fn set_runtime_id(&mut self, runtime: String, id: Option) { - self.runtime_ids.insert(runtime, id); - } - - pub fn get_runtime_id(&self, runtime: &str) -> Option { - self.runtime_ids.get(runtime).and_then(|v| v.to_owned()) - } - - pub fn set_chunk_modules_source_types( - &mut self, - chunk: &ChunkUkey, - module: ModuleIdentifier, - source_types: FxHashSet, - ) { - let cgc = self - .chunk_graph_chunk_by_chunk_ukey - .get_mut(chunk) - .expect("should have cgc"); - - if let Some(source_types_by_module) = &mut cgc.source_types_by_module { - source_types_by_module.insert(module, source_types); - } else { - let mut map = IdentifierMap::default(); - map.insert(module, source_types); - cgc.source_types_by_module = Some(map); - } - } - - pub fn get_chunk_module_source_types( - &self, - chunk: &ChunkUkey, - module: &ModuleRef, - module_graph: &ModuleGraph, - ) -> FxHashSet { - self - .chunk_graph_chunk_by_chunk_ukey - .get(chunk) - .and_then(|cgc| { - if let Some(source_types_by_module) = &cgc.source_types_by_module { - return source_types_by_module.get(&module.identifier()).cloned(); - } - - None - }) - .unwrap_or_else(|| module.source_types(module_graph).iter().copied().collect()) - } -} - -#[cfg(test)] -mod tests { - use super::ChunkId; - - #[test] - fn chunk_id_serialize_matches_runtime_numeric_rules() { - assert_eq!(simd_json::to_string(&ChunkId::from("903")).unwrap(), "903"); - assert_eq!( - simd_json::to_string(&ChunkId::from("01")).unwrap(), - "\"01\"" - ); - assert_eq!( - simd_json::to_string(&ChunkId::from("main")).unwrap(), - "\"main\"" - ); - assert_eq!( - simd_json::to_string(&ChunkId::from("4294967295")).unwrap(), - "4294967295" - ); - assert_eq!( - simd_json::to_string(&ChunkId::from("4294967296")).unwrap(), - "\"4294967296\"" - ); - assert_eq!( - simd_json::to_string(&vec![ChunkId::from("01"), ChunkId::from("903")]).unwrap(), - "[\"01\",903]" - ); - } } diff --git a/crates/rspack_core/src/chunk_graph/chunk_graph_module.rs b/crates/rspack_core/src/chunk_graph/chunk_graph_module.rs index 24a205f46ad8..8f30da4fd4d6 100644 --- a/crates/rspack_core/src/chunk_graph/chunk_graph_module.rs +++ b/crates/rspack_core/src/chunk_graph/chunk_graph_module.rs @@ -14,9 +14,9 @@ use serde::{Serialize, Serializer}; use ustr::Ustr; use crate::{ - AsyncDependenciesBlockIdentifier, ChunkByUkey, ChunkGraph, ChunkGroup, ChunkGroupByUkey, - ChunkGroupUkey, ChunkUkey, Compilation, Module, ModuleGraph, ModuleIdentifier, ModuleIdsArtifact, - RuntimeGlobals, RuntimeSpec, RuntimeSpecMap, RuntimeSpecSet, for_each_runtime, get_runtime_key, + AsyncDependenciesBlockIdentifier, ChunkGraph, ChunkGroup, ChunkGroupByUkey, ChunkGroupUkey, + ChunkUkey, Compilation, Module, ModuleGraph, ModuleIdentifier, ModuleIdsArtifact, RuntimeGlobals, + RuntimeSpec, RuntimeSpecMap, RuntimeSpecSet, for_each_runtime, get_runtime_key, }; pub type ModuleIdMap = @@ -97,7 +97,7 @@ impl ChunkGraphModule { } } -impl ChunkGraph { +impl super::ChunkGraphTopology { pub fn modules(&self) -> IdentifierSet { self .chunk_graph_module_by_module_identifier @@ -206,71 +206,6 @@ impl ChunkGraph { .map_or(0, |cgm| cgm.chunks.len()) } - pub fn set_module_runtime_requirements( - compilation: &mut Compilation, - module_identifier: ModuleIdentifier, - map: RuntimeSpecMap, - ) { - compilation - .cgm_runtime_requirements_artifact - .set_runtime_requirements(module_identifier, map); - } - - pub fn get_module_runtime_requirements<'c>( - compilation: &'c Compilation, - module_identifier: ModuleIdentifier, - runtime: &RuntimeSpec, - ) -> Option<&'c RuntimeGlobals> { - compilation - .cgm_runtime_requirements_artifact - .get(&module_identifier, runtime) - } - - pub fn get_module_runtimes( - &self, - module_identifier: ModuleIdentifier, - chunk_by_ukey: &ChunkByUkey, - ) -> RuntimeSpecSet { - let cgm = self.expect_chunk_graph_module(module_identifier); - let mut runtimes = RuntimeSpecSet::default(); - for chunk_ukey in cgm.chunks.iter() { - let chunk = chunk_by_ukey.expect_get(chunk_ukey); - runtimes.set(chunk.runtime().clone()); - } - runtimes - } - - pub fn get_module_runtimes_iter<'a>( - &self, - module_identifier: ModuleIdentifier, - chunk_by_ukey: &'a ChunkByUkey, - ) -> impl Iterator + use<'a, '_> { - let cgm = self.expect_chunk_graph_module(module_identifier); - cgm.chunks.iter().map(|chunk_ukey| { - let chunk = chunk_by_ukey.expect_get(chunk_ukey); - chunk.runtime() - }) - } - - pub fn get_module_id( - module_ids: &ModuleIdsArtifact, - module_identifier: ModuleIdentifier, - ) -> Option<&ModuleId> { - module_ids.get(&module_identifier) - } - - pub fn set_module_id( - module_ids: &mut ModuleIdsArtifact, - module_identifier: ModuleIdentifier, - id: ModuleId, - ) -> bool { - if let Some(old_id) = module_ids.insert(module_identifier, id.clone()) { - old_id != id - } else { - true - } - } - pub fn get_block_chunk_group<'a>( &self, block: &AsyncDependenciesBlockIdentifier, @@ -290,26 +225,6 @@ impl ChunkGraph { self.block_to_chunk_group_ukey.insert(block, chunk_group); } - pub fn get_module_hash<'c>( - compilation: &'c Compilation, - module_identifier: ModuleIdentifier, - runtime: &RuntimeSpec, - ) -> Option<&'c RspackHashDigest> { - compilation - .cgm_hash_artifact - .get(&module_identifier, runtime) - } - - pub fn set_module_hashes( - compilation: &mut Compilation, - module_identifier: ModuleIdentifier, - hashes: RuntimeSpecMap, - ) -> bool { - compilation - .cgm_hash_artifact - .set_hashes(module_identifier, hashes) - } - pub fn try_get_module_chunks( &self, module_identifier: &ModuleIdentifier, @@ -419,7 +334,7 @@ impl ChunkGraph { let mut hasher = FxHasher::default(); let module_identifier = module.identifier(); - Self::get_module_id(&compilation.module_ids_artifact, module_identifier) + ChunkGraph::get_module_id(&compilation.module_ids_artifact, module_identifier) .dyn_hash(&mut hasher); module.source_types(mg).dyn_hash(&mut hasher); ModuleGraph::is_async(&compilation.async_modules_artifact, &module_identifier) @@ -434,3 +349,83 @@ impl ChunkGraph { ) } } + +impl ChunkGraph { + pub fn set_module_runtime_requirements( + compilation: &mut Compilation, + module_identifier: ModuleIdentifier, + map: RuntimeSpecMap, + ) { + compilation + .cgm_runtime_requirements_artifact + .set_runtime_requirements(module_identifier, map); + } + pub fn get_module_runtime_requirements<'c>( + compilation: &'c Compilation, + module_identifier: ModuleIdentifier, + runtime: &RuntimeSpec, + ) -> Option<&'c RuntimeGlobals> { + compilation + .cgm_runtime_requirements_artifact + .get(&module_identifier, runtime) + } + pub fn get_module_id( + module_ids: &ModuleIdsArtifact, + module_identifier: ModuleIdentifier, + ) -> Option<&ModuleId> { + module_ids.get(&module_identifier) + } + pub fn set_module_id( + module_ids: &mut ModuleIdsArtifact, + module_identifier: ModuleIdentifier, + id: ModuleId, + ) -> bool { + if let Some(old_id) = module_ids.insert(module_identifier, id.clone()) { + old_id != id + } else { + true + } + } + pub fn get_module_hash<'c>( + compilation: &'c Compilation, + module_identifier: ModuleIdentifier, + runtime: &RuntimeSpec, + ) -> Option<&'c RspackHashDigest> { + compilation + .cgm_hash_artifact + .get(&module_identifier, runtime) + } + pub fn set_module_hashes( + compilation: &mut Compilation, + module_identifier: ModuleIdentifier, + hashes: RuntimeSpecMap, + ) -> bool { + compilation + .cgm_hash_artifact + .set_hashes(module_identifier, hashes) + } +} + +impl ChunkGraph { + pub fn get_module_runtimes(&self, module_identifier: ModuleIdentifier) -> RuntimeSpecSet { + let chunk_by_ukey = &self.chunks; + let cgm = self.expect_chunk_graph_module(module_identifier); + let mut runtimes = RuntimeSpecSet::default(); + for chunk_ukey in cgm.chunks.iter() { + let chunk = chunk_by_ukey.expect_get(chunk_ukey); + runtimes.set(chunk.runtime().clone()); + } + runtimes + } + pub fn get_module_runtimes_iter<'a>( + &'a self, + module_identifier: ModuleIdentifier, + ) -> impl Iterator + use<'a> { + let chunk_by_ukey = &self.chunks; + let cgm = self.expect_chunk_graph_module(module_identifier); + cgm.chunks.iter().map(|chunk_ukey| { + let chunk = chunk_by_ukey.expect_get(chunk_ukey); + chunk.runtime() + }) + } +} diff --git a/crates/rspack_core/src/chunk_graph/map.rs b/crates/rspack_core/src/chunk_graph/map.rs new file mode 100644 index 000000000000..a5d72608cbc0 --- /dev/null +++ b/crates/rspack_core/src/chunk_graph/map.rs @@ -0,0 +1,89 @@ +use std::ops::{Deref, DerefMut}; + +use rayon::iter::ParallelIterator; + +use super::ChunkSlotMap; +use crate::{Chunk, ChunkUkey}; + +/// Array-backed working data for the current Chunks of a single graph. +/// +/// Uses the same storage as ChunkSlotMap, but cannot allocate Chunk identities. +/// Insertion validates membership against the owning graph's Chunk table before +/// replacing a slot. This prevents stale keys from evicting data for a reused +/// slot. Keep historical identities and sparse collections in HashMap instead. +#[derive(Debug)] +pub struct ChunkMap { + values: ChunkSlotMap, +} + +impl Default for ChunkMap { + fn default() -> Self { + Self::with_capacity(0) + } +} + +impl ChunkMap { + /// Reserve using the owning table's slot_count, not its live Chunk count. + /// During graph construction this can also include an estimate of new Chunks. + pub fn with_capacity(capacity: usize) -> Self { + Self { + values: ChunkSlotMap::with_capacity(capacity), + } + } + + /// Associate data with a current Chunk. Panics for a stale or foreign key. + pub fn insert(&mut self, chunks: &ChunkSlotMap, key: ChunkUkey, value: T) -> Option { + assert!( + chunks.contains(&key), + "Cannot associate data with a non-member Chunk" + ); + self.values.insert(key, value) + } + + /// Remove only this exact identity. Also usable after the Chunk was deleted. + pub fn remove(&mut self, key: &ChunkUkey) -> Option { + self.values.remove(key) + } + + pub(super) fn retain(&mut self, keep: impl FnMut(&ChunkUkey, &mut T) -> bool) { + self.values.retain(keep); + } + + pub(crate) fn into_entries(self) -> impl Iterator { + self.values.into_entries() + } + + pub(crate) fn par_iter_mut(&mut self) -> impl ParallelIterator + where + T: Send, + { + self.values.par_iter_mut() + } + + pub fn get_or_insert_default(&mut self, chunks: &ChunkSlotMap, key: ChunkUkey) -> &mut T + where + T: Default, + { + assert!( + chunks.contains(&key), + "Cannot associate data with a non-member Chunk" + ); + if !self.values.contains(&key) { + self.values.insert(key, T::default()); + } + self.values.expect_get_mut(&key) + } +} + +impl Deref for ChunkMap { + type Target = ChunkSlotMap; + fn deref(&self) -> &Self::Target { + &self.values + } +} + +impl DerefMut for ChunkMap { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.values + } +} diff --git a/crates/rspack_core/src/chunk_graph/mod.rs b/crates/rspack_core/src/chunk_graph/mod.rs index 79f3e46631a1..dea93a049d0a 100644 --- a/crates/rspack_core/src/chunk_graph/mod.rs +++ b/crates/rspack_core/src/chunk_graph/mod.rs @@ -3,25 +3,142 @@ use rustc_hash::FxHashMap as HashMap; use crate::{AsyncDependenciesBlockIdentifierMap, ChunkGroupUkey, ChunkUkey, ModuleIdentifier}; +mod map; +pub use map::ChunkMap; +mod set; +pub use set::ChunkSet; + +mod slot_map; +pub use slot_map::ChunkSlotMap; + pub mod chunk_graph_chunk; pub mod chunk_graph_module; pub use chunk_graph_chunk::{ChunkGraphChunk, ChunkIdMap, ChunkSizeOptions, IndexChunkIdMap}; pub use chunk_graph_module::{ChunkGraphModule, ModuleId, ModuleIdMap}; +/// Relationships kept separate from Chunk values for disjoint field borrowing. +/// Clone copies relationships as part of a graph snapshot. #[derive(Debug, Clone, Default)] -pub struct ChunkGraph { +pub struct ChunkGraphTopology { /// If a module is imported dynamically, it will be assigned to a unique ChunkGroup pub(crate) block_to_chunk_group_ukey: AsyncDependenciesBlockIdentifierMap, pub(crate) chunk_graph_module_by_module_identifier: IdentifierMap, - chunk_graph_chunk_by_chunk_ukey: HashMap, + chunk_graph_chunk_by_chunk_ukey: ChunkSlotMap, runtime_ids: HashMap>, } -impl ChunkGraph { +impl ChunkGraphTopology { pub fn is_entry_module(&self, module_id: &ModuleIdentifier) -> bool { let cgm = self.expect_chunk_graph_module(*module_id); !cgm.entry_in_chunks.is_empty() } } + +/// Owns Chunk storage and relationships. Cloning copies a graph snapshot while +/// preserving existing keys; subsequent allocations receive fresh identities. +#[derive(Debug, Clone, Default)] +pub struct ChunkGraph { + pub chunks: ChunkSlotMap, + pub topology: ChunkGraphTopology, + // Allocation belongs to the graph, never to its individual value tables. + free_slots: Vec, +} + +impl std::ops::Deref for ChunkGraph { + type Target = ChunkGraphTopology; + fn deref(&self) -> &Self::Target { + &self.topology + } +} +impl std::ops::DerefMut for ChunkGraph { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.topology + } +} + +impl ChunkGraph { + pub fn create_chunk( + &mut self, + name: Option, + kind: crate::ChunkKind, + ) -> rspack_error::Result { + let index = match self.free_slots.last() { + Some(index) => *index, + None => u32::try_from(self.chunks.slot_count()) + .map_err(|_| rspack_error::error!("Chunk slot space exhausted"))?, + }; + // Allocate before changing either table: identity exhaustion leaves the graph intact. + let key = ChunkUkey::allocate(index)?; + self.free_slots.pop(); + self.chunks.insert(key, crate::Chunk::new(key, name, kind)); + self + .topology + .chunk_graph_chunk_by_chunk_ukey + .insert(key, ChunkGraphChunk::default()); + Ok(key) + } + + /// Add the target to the source's groups and merge its runtime and name hints. + /// Collect first, then apply: the source Chunk only needs an immutable borrow. + pub fn split_chunk( + &mut self, + source: &ChunkUkey, + target: &ChunkUkey, + groups: &mut crate::ChunkGroupByUkey, + ) { + assert_ne!(source, target, "Cannot split a Chunk into itself"); + // Validate both handles before updating any group relationships. + self.chunks.expect_get(target); + let source = self.chunks.expect_get(source); + let mut data = + crate::ChunkSplitData::with_capacity(source.groups().len(), source.id_name_hints().len()); + source.split_collect_new_chunk_data(*target, groups, &mut data); + data.apply_to(self.chunks.expect_get_mut(target)); + } + + pub(crate) fn remove_chunk( + &mut self, + key: &ChunkUkey, + groups: &mut crate::ChunkGroupByUkey, + ) -> Option { + // Both tables share membership. Removing the topology row validates the + // complete key, including stale identities after slot reuse. + let relations = self.topology.chunk_graph_chunk_by_chunk_ukey.remove(key)?; + for module in relations + .modules + .iter() + .chain(relations.entry_modules.keys()) + .chain(relations.runtime_modules.iter()) + { + if let Some(data) = self + .topology + .chunk_graph_module_by_module_identifier + .get_mut(module) + { + data.chunks.remove(key); + data.entry_in_chunks.remove(key); + data.runtime_in_chunks.remove(key); + } + } + let mut chunk = self.chunks.remove(key).expect("live Chunk"); + chunk.disconnect_from_groups(groups); + self.free_slots.push(key.index() as u32); + Some(chunk) + } +} + +impl ChunkGraph { + pub fn integrate_chunks( + &mut self, + a: &ChunkUkey, + b: &ChunkUkey, + groups: &mut crate::ChunkGroupByUkey, + modules: &crate::ModuleGraph, + ) { + self + .topology + .integrate_chunks(a, b, &mut self.chunks, groups, modules); + } +} diff --git a/crates/rspack_core/src/chunk_graph/set.rs b/crates/rspack_core/src/chunk_graph/set.rs new file mode 100644 index 000000000000..37f4586cff4d --- /dev/null +++ b/crates/rspack_core/src/chunk_graph/set.rs @@ -0,0 +1,52 @@ +use super::{ChunkMap, ChunkSlotMap}; +use crate::{Chunk, ChunkUkey}; + +/// Array-backed membership for current Chunks of one graph. +/// +/// Stores the complete key, so removing a stale identity cannot erase the new +/// occupant of its slot. Historical identities need a HashSet instead. +#[derive(Debug, Default)] +pub struct ChunkSet { + values: ChunkMap<()>, +} + +impl ChunkSet { + /// Reserve by slot high-water mark, including vacant slots, not live count. + pub fn with_capacity(capacity: usize) -> Self { + Self { + values: ChunkMap::with_capacity(capacity), + } + } + + pub fn insert(&mut self, chunks: &ChunkSlotMap, key: ChunkUkey) -> bool { + self.values.insert(chunks, key, ()).is_none() + } + + pub fn remove(&mut self, key: &ChunkUkey) -> bool { + self.values.remove(key).is_some() + } + + pub fn contains(&self, key: &ChunkUkey) -> bool { + self.values.contains(key) + } + + pub fn len(&self) -> usize { + self.values.len() + } + + pub fn is_empty(&self) -> bool { + self.values.is_empty() + } + + pub fn iter(&self) -> impl Iterator { + self.values.keys() + } + + pub fn retain(&mut self, mut keep: impl FnMut(&ChunkUkey) -> bool) { + self.values.retain(|key, _| keep(key)); + } + + pub fn into_keys(self) -> impl Iterator { + self.values.into_entries().map(|(key, ())| key) + } +} diff --git a/crates/rspack_core/src/chunk_graph/slot_map.rs b/crates/rspack_core/src/chunk_graph/slot_map.rs new file mode 100644 index 000000000000..de013e012923 --- /dev/null +++ b/crates/rspack_core/src/chunk_graph/slot_map.rs @@ -0,0 +1,152 @@ +use rayon::prelude::*; + +use crate::ChunkUkey; + +/// Directly indexed values for one graph's current Chunk identities. +/// +/// Like slotmap's SecondaryMap, each slot is either vacant or contains its +/// identity and payload. ChunkUkey's NonZeroU32 identity provides a niche for +/// Option, including for payloads with no niche of their own. Allocation and +/// recycling belong to ChunkGraph; the two tables cannot diverge in free lists. +/// Clone copies keys and values for graph snapshots, never the identity allocator. +#[derive(Debug, Clone)] +pub struct ChunkSlotMap { + slots: Vec>, + len: usize, +} + +impl Default for ChunkSlotMap { + fn default() -> Self { + Self::with_capacity(0) + } +} + +impl ChunkSlotMap { + /// Reserve slots without initializing them. Capacity is a slot high-water + /// mark, not a live count: deleted slots retain their index. + pub fn with_capacity(capacity: usize) -> Self { + Self { + slots: Vec::with_capacity(capacity), + len: 0, + } + } + + /// Number of addressable slots, including vacant ones. + pub fn slot_count(&self) -> usize { + self.slots.len() + } + + #[inline] + pub fn get(&self, key: &ChunkUkey) -> Option<&T> { + let (stored, value) = self.slots.get(key.index())?.as_ref()?; + (stored == key).then_some(value) + } + + #[inline] + pub fn get_mut(&mut self, key: &ChunkUkey) -> Option<&mut T> { + let (stored, value) = self.slots.get_mut(key.index())?.as_mut()?; + (stored == key).then_some(value) + } + + pub fn expect_get(&self, key: &ChunkUkey) -> &T { + self + .get(key) + .unwrap_or_else(|| panic!("Chunk({key:?}) not found in ChunkSlotMap")) + } + + pub fn expect_get_mut(&mut self, key: &ChunkUkey) -> &mut T { + self + .get_mut(key) + .unwrap_or_else(|| panic!("Chunk({key:?}) not found in ChunkSlotMap")) + } + + pub(super) fn insert(&mut self, key: ChunkUkey, value: T) -> Option { + let index = key.index(); + if index >= self.slots.len() { + // Sequential insertion uses Vec's amortized growth. Only secondary maps + // can skip indices; initialize just their missing prefix, never capacity. + if index > self.slots.len() { + self.slots.reserve(index + 1 - self.slots.len()); + self.slots.resize_with(index, || None); + } + self.slots.push(Some((key, value))); + self.len += 1; + return None; + } + let old = self.slots[index].replace((key, value)); + if old.is_none() { + self.len += 1; + } + old + .filter(|(stored, _)| *stored == key) + .map(|(_, value)| value) + } + + pub(super) fn remove(&mut self, key: &ChunkUkey) -> Option { + let slot = self.slots.get_mut(key.index())?; + if slot.as_ref()?.0 != *key { + return None; + } + let (_, value) = slot.take()?; + self.len -= 1; + Some(value) + } + + #[inline] + pub fn contains(&self, key: &ChunkUkey) -> bool { + self.get(key).is_some() + } + pub fn len(&self) -> usize { + self.len + } + pub fn is_empty(&self) -> bool { + self.len == 0 + } + pub fn iter(&self) -> impl Iterator { + self + .slots + .iter() + .filter_map(|slot| slot.as_ref().map(|(key, value)| (key, value))) + } + pub fn iter_mut(&mut self) -> impl Iterator { + self + .slots + .iter_mut() + .filter_map(|slot| slot.as_mut().map(|(key, value)| (&*key, value))) + } + pub(super) fn retain(&mut self, mut keep: impl FnMut(&ChunkUkey, &mut T) -> bool) { + for slot in &mut self.slots { + if let Some((key, value)) = slot + && !keep(key, value) + { + let removed = slot.take(); + self.len -= 1; + drop(removed); + } + } + } + + pub(super) fn into_entries(self) -> impl Iterator { + self.slots.into_iter().flatten() + } + + pub(super) fn par_iter_mut(&mut self) -> impl ParallelIterator + where + T: Send, + { + self + .slots + .par_iter_mut() + .filter_map(|slot| slot.as_mut().map(|(key, value)| (&*key, value))) + } + + pub fn keys(&self) -> impl Iterator { + self.iter().map(|(key, _)| key) + } + pub fn values(&self) -> impl Iterator { + self.iter().map(|(_, value)| value) + } + pub fn values_mut(&mut self) -> impl Iterator { + self.iter_mut().map(|(_, value)| value) + } +} diff --git a/crates/rspack_core/src/chunk_group.rs b/crates/rspack_core/src/chunk_group.rs index a2bcbfac733d..ad92df8aefa3 100644 --- a/crates/rspack_core/src/chunk_group.rs +++ b/crates/rspack_core/src/chunk_group.rs @@ -9,7 +9,7 @@ use rspack_util::fx_hash::FxIndexSet; use rustc_hash::FxHashSet; use crate::{ - Chunk, ChunkByUkey, ChunkGroupByUkey, ChunkGroupUkey, ChunkLoading, ChunkUkey, Compilation, + Chunk, ChunkGroupByUkey, ChunkGroupUkey, ChunkLoading, ChunkSlotMap, ChunkUkey, Compilation, DependencyLocation, DynamicImportFetchPriority, Filename, LibraryOptions, ModuleIdentifier, ModuleLayer, PublicPath, WasmLoading, }; @@ -101,7 +101,7 @@ impl ChunkGroup { .copied() } - pub fn get_files(&self, chunk_by_ukey: &ChunkByUkey) -> Vec { + pub fn get_files(&self, chunk_by_ukey: &ChunkSlotMap) -> Vec { self .chunks .iter() @@ -159,6 +159,11 @@ impl ChunkGroup { self.entrypoint_chunk = Some(chunk_ukey); } + /// An empty entry Chunk may have been removed after an entry failed to resolve. + pub fn is_entrypoint_chunk(&self, chunk_ukey: &ChunkUkey) -> bool { + self.entrypoint_chunk.as_ref() == Some(chunk_ukey) + } + pub fn get_entrypoint_chunk(&self) -> ChunkUkey { match self.kind { ChunkGroupKind::Entrypoint { .. } => self @@ -220,6 +225,12 @@ impl ChunkGroup { } pub fn remove_chunk(&mut self, chunk: &ChunkUkey) -> bool { + if self.runtime_chunk == Some(*chunk) { + self.runtime_chunk = None; + } + if self.entrypoint_chunk == Some(*chunk) { + self.entrypoint_chunk = None; + } let idx = self.chunks.iter().position(|ukey| ukey == chunk); if let Some(idx) = idx { self.chunks.remove(idx); @@ -278,7 +289,8 @@ impl ChunkGroup { .filter_map(|chunk| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk) .and_then(|item| item.id()) }) diff --git a/crates/rspack_core/src/compilation/assign_runtime_ids/mod.rs b/crates/rspack_core/src/compilation/assign_runtime_ids/mod.rs index a06139017197..668d761fd059 100644 --- a/crates/rspack_core/src/compilation/assign_runtime_ids/mod.rs +++ b/crates/rspack_core/src/compilation/assign_runtime_ids/mod.rs @@ -16,8 +16,8 @@ impl PassExt for AssignRuntimeIdsPass { fn process_entrypoint( entrypoint_ukey: &ChunkGroupUkey, chunk_group_by_ukey: &ChunkGroupByUkey, - chunk_by_ukey: &ChunkByUkey, - chunk_graph: &mut ChunkGraph, + chunk_by_ukey: &ChunkSlotMap, + chunk_graph: &mut crate::chunk_graph::ChunkGraphTopology, ) { let entrypoint = chunk_group_by_ukey.expect_get(entrypoint_ukey); let runtime = entrypoint @@ -40,8 +40,8 @@ impl PassExt for AssignRuntimeIdsPass { process_entrypoint( i.1, &compilation.build_chunk_graph_artifact.chunk_group_by_ukey, - &compilation.build_chunk_graph_artifact.chunk_by_ukey, - &mut compilation.build_chunk_graph_artifact.chunk_graph, + &compilation.build_chunk_graph_artifact.chunk_graph.chunks, + &mut compilation.build_chunk_graph_artifact.chunk_graph.topology, ) } for i in compilation @@ -52,8 +52,8 @@ impl PassExt for AssignRuntimeIdsPass { process_entrypoint( i, &compilation.build_chunk_graph_artifact.chunk_group_by_ukey, - &compilation.build_chunk_graph_artifact.chunk_by_ukey, - &mut compilation.build_chunk_graph_artifact.chunk_graph, + &compilation.build_chunk_graph_artifact.chunk_graph.chunks, + &mut compilation.build_chunk_graph_artifact.chunk_graph.topology, ) } diff --git a/crates/rspack_core/src/compilation/build_chunk_graph/code_splitter.rs b/crates/rspack_core/src/compilation/build_chunk_graph/code_splitter.rs index 0f912ef85a7d..35d2a3995e32 100644 --- a/crates/rspack_core/src/compilation/build_chunk_graph/code_splitter.rs +++ b/crates/rspack_core/src/compilation/build_chunk_graph/code_splitter.rs @@ -19,10 +19,11 @@ use super::incremental::ChunkCreateData; use crate::{ AsyncDependenciesBlockIdentifier, AsyncDependenciesBlockIdentifierMap, AsyncDependenciesBlockIdentifierSet, ChunkGroup, ChunkGroupKind, ChunkGroupOptions, - ChunkGroupUkey, ChunkLoading, ChunkUkey, Compilation, ConnectionState, DependenciesBlock, - DependencyId, DependencyLocation, EntryDependency, EntryRuntime, ExportsInfoArtifact, - GroupOptions, Logger, ModuleDependency, ModuleGraph, ModuleGraphCacheArtifact, ModuleIdentifier, - RuntimeSpec, SideEffectsStateArtifact, SyntheticDependencyLocation, assign_depths, + ChunkGroupUkey, ChunkLoading, ChunkMap, ChunkUkey, Compilation, ConnectionState, + DependenciesBlock, DependencyId, DependencyLocation, EntryDependency, EntryRuntime, + ExportsInfoArtifact, GroupOptions, Logger, ModuleDependency, ModuleGraph, + ModuleGraphCacheArtifact, ModuleIdentifier, RuntimeSpec, SideEffectsStateArtifact, + SyntheticDependencyLocation, assign_depths, dependencies_block::AsyncDependenciesToInitialChunkError, get_entry_runtime, incremental::{IncrementalPasses, Mutation}, @@ -215,7 +216,7 @@ impl ChunkGroupInfo { fn calculate_resulting_available_modules( &mut self, chunk_group: &ChunkGroup, - mask_by_chunk: &HashMap, + mask_by_chunk: &ChunkMap, ) { if self.resulting_available_modules.is_some() { return; @@ -351,7 +352,7 @@ pub(crate) struct CodeSplitter { pub(crate) named_async_entrypoints: HashMap, pub(crate) block_modules_runtime_map: BlockModulesRuntimeMap, pub(crate) ordinal_by_module: IdentifierMap, - pub(crate) mask_by_chunk: HashMap, + pub(crate) mask_by_chunk: ChunkMap, stat_processed_queue_items: u32, stat_processed_blocks: u32, @@ -626,19 +627,22 @@ impl CodeSplitter { }) .collect::>(); - let (chunk_ukey, created) = Compilation::add_named_chunk( - name.to_string(), - &mut compilation.build_chunk_graph_artifact.chunk_by_ukey, - &mut compilation.build_chunk_graph_artifact.named_chunks, - ); + let (chunk_ukey, created) = compilation + .build_chunk_graph_artifact + .add_named_chunk(name.to_string())?; if created && let Some(mut mutations) = compilation.incremental.mutations_write() { mutations.add(Mutation::ChunkAdd { chunk: chunk_ukey }); } - self.mask_by_chunk.insert(chunk_ukey, BigUint::from(0u32)); + self.mask_by_chunk.insert( + &compilation.build_chunk_graph_artifact.chunk_graph.chunks, + chunk_ukey, + BigUint::from(0u32), + ); let runtime = get_entry_runtime(name, options, &compilation.entries); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&chunk_ukey); let mut incremental_diagnostic = None; @@ -656,11 +660,6 @@ impl CodeSplitter { } } - compilation - .build_chunk_graph_artifact - .chunk_graph - .add_chunk(chunk_ukey); - let mut entrypoint = ChunkGroup::new(ChunkGroupKind::new_entrypoint( true, Box::new(options.clone()), @@ -726,6 +725,7 @@ impl CodeSplitter { compilation .build_chunk_graph_artifact .chunk_graph + .topology .add_module(*module_identifier); modules.push(*module_identifier); @@ -733,6 +733,7 @@ impl CodeSplitter { compilation .build_chunk_graph_artifact .chunk_graph + .topology .connect_chunk_and_entry_module(chunk.ukey(), *module_identifier, entrypoint.ukey); } @@ -810,13 +811,13 @@ Remove the 'runtime' option from the entrypoint." .chunk_group_by_ukey .expect_get(key) .get_entrypoint_chunk() - .as_u32() + .as_u64() }); runtime_errors.push(diagnostic); } if let Some(depend_on) = &options.depend_on { - let ukey = compilation + let ukey = *compilation .build_chunk_graph_artifact .entrypoints .get(name) @@ -831,10 +832,11 @@ Remove the 'runtime' option from the entrypoint." let entry_point = compilation .build_chunk_graph_artifact .chunk_group_by_ukey - .expect_get(ukey); + .expect_get(&ukey); let entry_point_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&entry_point.get_entrypoint_chunk()); let referenced_chunks = entry_point_chunk .get_all_referenced_chunks(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey); @@ -851,7 +853,7 @@ Remove the 'runtime' option from the entrypoint." let mut diagnostic = Diagnostic::from(error!( "Entrypoints '{name}' and '{dep}' use 'dependOn' to depend on each other in a circular way." )); - diagnostic.chunk = Some(entry_point.get_entrypoint_chunk().as_u32()); + diagnostic.chunk = Some(entry_point.get_entrypoint_chunk().as_u64()); runtime_errors.push(diagnostic); entry_point_runtime = Some(entry_point_chunk.ukey()); has_error = true; @@ -868,7 +870,7 @@ Remove the 'runtime' option from the entrypoint." let entry_point = compilation .build_chunk_graph_artifact .chunk_group_by_ukey - .expect_get_mut(ukey); + .expect_get_mut(&ukey); entry_point.set_runtime_chunk(entry_point_runtime.expect("Should set runtime chunk")); } else { { @@ -877,7 +879,7 @@ Remove the 'runtime' option from the entrypoint." .build_chunk_graph_artifact .chunk_group_by_ukey .expect_get_mut(depend); - if depend_chunk_group.add_child(*ukey) { + if depend_chunk_group.add_child(ukey) { entry_point_parents.push(*depend); } } @@ -885,7 +887,7 @@ Remove the 'runtime' option from the entrypoint." let entry_point = compilation .build_chunk_graph_artifact .chunk_group_by_ukey - .expect_get_mut(ukey); + .expect_get_mut(&ukey); for parent in entry_point_parents { entry_point.add_parent(parent); } @@ -893,17 +895,12 @@ Remove the 'runtime' option from the entrypoint." } else if let Some(EntryRuntime::String(runtime)) = &options.runtime && !runtime.is_empty() { - let ukey = compilation + let entrypoint_ukey = *compilation .build_chunk_graph_artifact .entrypoints .get(name) .ok_or_else(|| error!("no entrypoints found"))?; - let entry_point = compilation - .build_chunk_graph_artifact - .chunk_group_by_ukey - .expect_get_mut(ukey); - let chunk = match compilation .build_chunk_graph_artifact .named_chunks @@ -911,6 +908,10 @@ Remove the 'runtime' option from the entrypoint." { Some(ukey) => { if !self.runtime_chunks.contains(ukey) { + let entry_point = compilation + .build_chunk_graph_artifact + .chunk_group_by_ukey + .expect_get_mut(&entrypoint_ukey); let entry_chunk = entry_point.get_entrypoint_chunk(); let mut diagnostic = Diagnostic::from( error!( @@ -920,39 +921,43 @@ Did you mean to use 'dependOn: \"{runtime}\"' instead to allow using entrypoint Or do you want to use the entrypoints '{name}' and '{runtime}' independently on the same page with a shared runtime? In this case give them both the same value for the 'runtime' option. It must be a name not already used by an entrypoint." ), ); - diagnostic.chunk = Some(entry_chunk.as_u32()); + diagnostic.chunk = Some(entry_chunk.as_u64()); runtime_errors.push(diagnostic); entry_point.set_runtime_chunk(entry_chunk); } compilation .build_chunk_graph_artifact - .chunk_by_ukey - .expect_get_mut(ukey) + .chunk_graph + .chunks + .expect_get_mut(&ukey) } None => { - let (chunk_ukey, created) = Compilation::add_named_chunk( - runtime.clone(), - &mut compilation.build_chunk_graph_artifact.chunk_by_ukey, - &mut compilation.build_chunk_graph_artifact.named_chunks, - ); + let (chunk_ukey, created) = compilation + .build_chunk_graph_artifact + .add_named_chunk(runtime.clone())?; if created && let Some(mut mutations) = compilation.incremental.mutations_write() { mutations.add(Mutation::ChunkAdd { chunk: chunk_ukey }); } - self.mask_by_chunk.insert(chunk_ukey, BigUint::from(0u32)); + self.mask_by_chunk.insert( + &compilation.build_chunk_graph_artifact.chunk_graph.chunks, + chunk_ukey, + BigUint::from(0u32), + ); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&chunk_ukey); chunk.set_prevent_integration(true); - compilation - .build_chunk_graph_artifact - .chunk_graph - .add_chunk(chunk.ukey()); self.runtime_chunks.insert(chunk.ukey()); chunk } }; + let entry_point = compilation + .build_chunk_graph_artifact + .chunk_group_by_ukey + .expect_get_mut(&entrypoint_ukey); entry_point.unshift_chunk(chunk.ukey()); chunk.add_group(entry_point.ukey); entry_point.set_runtime_chunk(chunk.ukey()); @@ -1152,7 +1157,7 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on || !self.queue_delayed.is_empty() || !self.chunk_groups_for_combining.is_empty() { - self.process_queue(compilation); + self.process_queue(compilation)?; if !self.chunk_groups_for_combining.is_empty() { self.process_chunk_groups_for_combining(compilation); @@ -1162,7 +1167,7 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on self.process_connect_queue(compilation); if !self.chunk_groups_for_merging.is_empty() { - self.process_chunk_groups_for_merging(compilation); + self.process_chunk_groups_for_merging(compilation)?; } } @@ -1188,7 +1193,8 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on for chunk_ukey in chunk_group.chunks.iter() { if let Some(chunk) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get_mut(chunk_ukey) { chunk.set_runtime(merge_runtime(chunk.runtime(), &cgi.runtime)); @@ -1380,27 +1386,30 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on ctx.1 += 1; } - pub(crate) fn process_queue(&mut self, compilation: &mut Compilation) { + pub(crate) fn process_queue(&mut self, compilation: &mut Compilation) -> Result<()> { tracing::trace!("process_queue"); while let Some(action) = self.queue.pop() { self.stat_processed_queue_items += 1; match action { - QueueAction::AddAndEnterEntryModule(i) => self.add_and_enter_entry_module(&i, compilation), - QueueAction::AddAndEnterModule(i) => self.add_and_enter_module(&i, compilation), - QueueAction::_EnterModule(i) => self.enter_module(&i, compilation), - QueueAction::ProcessBlock(i) => self.process_block(&i, compilation), - QueueAction::ProcessEntryBlock(i) => self.process_entry_block(&i, compilation), + QueueAction::AddAndEnterEntryModule(i) => { + self.add_and_enter_entry_module(&i, compilation)? + } + QueueAction::AddAndEnterModule(i) => self.add_and_enter_module(&i, compilation)?, + QueueAction::_EnterModule(i) => self.enter_module(&i, compilation)?, + QueueAction::ProcessBlock(i) => self.process_block(&i, compilation)?, + QueueAction::ProcessEntryBlock(i) => self.process_entry_block(&i, compilation)?, QueueAction::LeaveModule(i) => self.leave_module(&i, compilation), } } + Ok(()) } fn add_and_enter_entry_module( &mut self, item: &AddAndEnterEntryModule, compilation: &mut Compilation, - ) { + ) -> Result<()> { tracing::trace!("add_and_enter_entry_module {:?}", item); let module_ordinal = *self.ordinal_by_module.get(&item.module).unwrap_or_else(|| { panic!( @@ -1416,19 +1425,20 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on .expect("chunk must in mask_by_chunk") .bit(module_ordinal) { - return; + return Ok(()); } let cgi = self.chunk_group_info_mut(&item.chunk_group_info); if cgi.min_available_modules.bit(module_ordinal) { cgi.skipped_items.insert(item.module); - return; + return Ok(()); } compilation .build_chunk_graph_artifact .chunk_graph + .topology .connect_chunk_and_entry_module(item.chunk, item.module, cgi.chunk_group); let chunk_mask = self .mask_by_chunk @@ -1443,17 +1453,22 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on chunk: item.chunk, }, compilation, - ) + )?; + Ok(()) } - fn add_and_enter_module(&mut self, item: &AddAndEnterModule, compilation: &mut Compilation) { + fn add_and_enter_module( + &mut self, + item: &AddAndEnterModule, + compilation: &mut Compilation, + ) -> Result<()> { tracing::trace!("add_and_enter_module {:?}", item); if compilation .build_chunk_graph_artifact .chunk_graph .is_module_in_chunk(&item.module, item.chunk) { - return; + return Ok(()); } // if this module in parent chunks @@ -1467,12 +1482,13 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on if cgi.min_available_modules.bit(module_ordinal) { cgi.skipped_items.insert(item.module); - return; + return Ok(()); } compilation .build_chunk_graph_artifact .chunk_graph + .topology .connect_chunk_and_module(item.chunk, item.module); let chunk_mask = self @@ -1488,10 +1504,11 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on chunk: item.chunk, }, compilation, - ) + )?; + Ok(()) } - fn enter_module(&mut self, item: &EnterModule, compilation: &mut Compilation) { + fn enter_module(&mut self, item: &EnterModule, compilation: &mut Compilation) -> Result<()> { tracing::trace!("enter_module {:?}", item); let cgi = self.chunk_group_info(&item.chunk_group_info); @@ -1533,7 +1550,8 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on block: item.module.into(), }, compilation, - ) + )?; + Ok(()) } fn leave_module(&mut self, item: &LeaveModule, compilation: &mut Compilation) { @@ -1565,7 +1583,11 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on } } - fn process_entry_block(&mut self, item: &ProcessEntryBlock, compilation: &mut Compilation) { + fn process_entry_block( + &mut self, + item: &ProcessEntryBlock, + compilation: &mut Compilation, + ) -> Result<()> { tracing::trace!("process_entry_block {:?}", item); self.stat_processed_blocks += 1; @@ -1636,11 +1658,12 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on item.chunk_group_info, item.chunk, compilation, - ); + )?; } + Ok(()) } - fn process_block(&mut self, item: &ProcessBlock, compilation: &mut Compilation) { + fn process_block(&mut self, item: &ProcessBlock, compilation: &mut Compilation) -> Result<()> { tracing::trace!("process_block {:?}", item); self.stat_processed_blocks += 1; @@ -1717,8 +1740,9 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on item.chunk_group_info, item.chunk, compilation, - ); + )?; } + Ok(()) } pub(crate) fn make_chunk_group( @@ -1728,12 +1752,12 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on item_chunk_group_info_ukey: CgiUkey, item_chunk_ukey: ChunkUkey, compilation: &mut Compilation, - ) { + ) -> Result<()> { self.edges.insert(block_id, module_id); let Some(item_chunk_group_info) = self.chunk_group_infos.get_mut(&item_chunk_group_info_ukey) else { - return; + return Ok(()); }; self @@ -1792,7 +1816,7 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on chunk_group_info: item_chunk_group_info_ukey, chunk: item_chunk_ukey, })); - return; + return Ok(()); } else { let chunk_ukey = if let Some(chunk_name) = compilation .get_module_graph() @@ -1801,28 +1825,28 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on .get_group_options() .and_then(|x| x.name()) { - let (chunk_ukey, created) = Compilation::add_named_chunk( - chunk_name.to_string(), - &mut compilation.build_chunk_graph_artifact.chunk_by_ukey, - &mut compilation.build_chunk_graph_artifact.named_chunks, - ); + let (chunk_ukey, created) = compilation + .build_chunk_graph_artifact + .add_named_chunk(chunk_name.to_string())?; if created && let Some(mut mutations) = compilation.incremental.mutations_write() { mutations.add(Mutation::ChunkAdd { chunk: chunk_ukey }); } chunk_ukey } else { - let chunk_ukey = - Compilation::add_chunk(&mut compilation.build_chunk_graph_artifact.chunk_by_ukey); + let chunk_ukey = compilation + .build_chunk_graph_artifact + .chunk_graph + .create_chunk(None, crate::ChunkKind::Normal)?; if let Some(mut mutations) = compilation.incremental.mutations_write() { mutations.add(Mutation::ChunkAdd { chunk: chunk_ukey }); } chunk_ukey }; - compilation - .build_chunk_graph_artifact - .chunk_graph - .add_chunk(chunk_ukey); - self.mask_by_chunk.insert(chunk_ukey, BigUint::from(0u32)); + self.mask_by_chunk.insert( + &compilation.build_chunk_graph_artifact.chunk_graph.chunks, + chunk_ukey, + BigUint::from(0u32), + ); let module_graph = compilation.get_module_graph(); let block = module_graph .block_by_id(&block_id) @@ -1852,7 +1876,8 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on let entry_options = entry_options.clone(); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&chunk_ukey); if let Some(filename) = &entry_options.filename { chunk.set_filename_template(Some(filename.clone())); @@ -1975,7 +2000,8 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on let chunk_group_ukey = chunk_group.ukey; let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&chunk_ukey); self.stat_chunk_group_created += 1; @@ -2054,6 +2080,7 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on DependenciesBlockIdentifier::AsyncDependenciesBlock(block_id), ); } + Ok(()) } #[allow(clippy::rc_buffer)] @@ -2439,7 +2466,7 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on self.chunk_groups_for_combining.clear(); } - fn process_chunk_groups_for_merging(&mut self, compilation: &mut Compilation) { + fn process_chunk_groups_for_merging(&mut self, compilation: &mut Compilation) -> Result<()> { self.stat_processed_chunk_groups_for_merging += self.chunk_groups_for_merging.len() as u32; let chunk_groups_for_merging = std::mem::take(&mut self.chunk_groups_for_merging); let mut chunk_groups_merging_batches: Vec)>> = vec![vec![]]; @@ -2539,7 +2566,7 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on self.chunk_group_info_mut(&info_ukey).initialized = true; // check if we can use cache to initialize it - if !initialized && self.recover_from_cache(info_ukey, compilation) { + if !initialized && self.recover_from_cache(info_ukey, compilation)? { self.stat_use_cache += 1; continue; } @@ -2550,6 +2577,7 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on } } } + Ok(()) } pub fn prepare( @@ -2558,6 +2586,18 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on compilation: &Compilation, ) -> Result<()> { let mg = compilation.get_module_graph(); + if self.mask_by_chunk.is_empty() { + // One Chunk per async block plus entry/runtime Chunks. Existing slots may + // include holes, so reserve from their high-water mark rather than len(). + let capacity = compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .slot_count() + + mg.blocks().len() + + compilation.entries.len() * 2; + self.mask_by_chunk = ChunkMap::with_capacity(capacity); + } self.prepared_connection_map = all_modules .par_iter() .filter_map(|module| prepare_module_connection_map(*module, mg).map(|map| (*module, map))) diff --git a/crates/rspack_core/src/compilation/build_chunk_graph/incremental.rs b/crates/rspack_core/src/compilation/build_chunk_graph/incremental.rs index e8c40a1ccda1..466b1794ec2b 100644 --- a/crates/rspack_core/src/compilation/build_chunk_graph/incremental.rs +++ b/crates/rspack_core/src/compilation/build_chunk_graph/incremental.rs @@ -50,7 +50,7 @@ impl ChunkReCreation { create_data.cgi, create_data.chunk, compilation, - ); + )?; Ok(()) } } @@ -63,7 +63,7 @@ impl CodeSplitter { module: ModuleIdentifier, compilation: &mut Compilation, ) -> Result> { - let chunk_graph = &mut compilation.build_chunk_graph_artifact.chunk_graph; + let chunk_graph = &mut compilation.build_chunk_graph_artifact.chunk_graph.topology; // Step 1. find all invalidate chunk groups and remove module from ChunkGraph let Some(cgm) = chunk_graph.get_chunk_graph_module_mut(module) else { @@ -76,7 +76,8 @@ impl CodeSplitter { .flat_map(|chunk| { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk); chunk.groups().clone() }) @@ -172,43 +173,32 @@ impl CodeSplitter { } } - let chunk_graph = &mut compilation.build_chunk_graph_artifact.chunk_graph; - // remove cgc and cgm for chunk_ukey in chunk_group.chunks.iter() { let Some(chunk) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get_mut(chunk_ukey) else { continue; }; if chunk.remove_group(&chunk_group_ukey) && chunk.groups().is_empty() { - // remove orphan chunk - if let Some(name) = chunk.name() { - compilation - .build_chunk_graph_artifact - .named_chunks - .remove(name); - } + let modules = compilation + .build_chunk_graph_artifact + .chunk_graph + .get_chunk_modules_identifier(chunk_ukey) + .clone(); compilation .build_chunk_graph_artifact - .chunk_by_ukey - .remove(chunk_ukey); - - // remove cgc and cgm - if let Some(chunk_graph_chunk) = chunk_graph.remove_chunk(chunk_ukey) { - for &module_identifier in chunk_graph_chunk.modules() { - let Some(cgm) = chunk_graph.get_chunk_graph_module_mut(module_identifier) else { - continue; - }; - - if cgm.chunks.remove(chunk_ukey) && cgm.chunks.is_empty() { - chunk_graph.remove_module(module_identifier) - } + .remove_chunk(chunk_ukey); + let chunk_graph = &mut compilation.build_chunk_graph_artifact.chunk_graph; + for module in modules { + if chunk_graph.get_module_chunks(module).is_empty() { + chunk_graph.remove_module(module); } - }; + } // remove mask self.mask_by_chunk.remove(chunk_ukey); @@ -338,7 +328,8 @@ impl CodeSplitter { for chunk_ukey in &cgm.chunks { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); chunk_groups.extend(chunk.groups().iter().copied()); } @@ -387,19 +378,23 @@ impl CodeSplitter { Ok(()) } - pub fn recover_from_cache(&mut self, cgi_ukey: CgiUkey, compilation: &mut Compilation) -> bool { + pub fn recover_from_cache( + &mut self, + cgi_ukey: CgiUkey, + compilation: &mut Compilation, + ) -> Result { if !compilation .incremental .mutations_readable(IncrementalPasses::BUILD_CHUNK_GRAPH) { - return false; + return Ok(false); } let Some(cgi) = self.chunk_group_infos.get(&cgi_ukey) else { - return false; + return Ok(false); }; let Some(blocks) = self.incoming_blocks_by_cgi.get(&cgi.ukey) else { - return false; + return Ok(false); }; let cg = compilation @@ -415,19 +410,19 @@ impl CodeSplitter { .as_async() .and_then(|block_id| self.chunk_caches.get(&block_id).cloned()) else { - return false; + return Ok(false); }; if !cache.can_rebuild { self.stat_cache_miss_by_cant_rebuild += 1; - return false; + return Ok(false); } let module_graph = compilation.get_module_graph(); let DependenciesBlockIdentifier::AsyncDependenciesBlock(block_id) = blocks.iter().next().expect("should have one block") else { - return false; + return Ok(false); }; let block = module_graph @@ -443,7 +438,7 @@ impl CodeSplitter { ) { self.stat_cache_miss_by_available_modules += 1; - return false; + return Ok(false); } let cache_result = cache @@ -463,7 +458,9 @@ impl CodeSplitter { let ordinal = self.get_module_ordinal(*module); chunk_graph.connect_chunk_and_module(chunk, *module); - let mask = self.mask_by_chunk.entry(chunk).or_default(); + let mask = self + .mask_by_chunk + .get_or_insert_default(&chunk_graph.chunks, chunk); mask.set_bit(ordinal, true); } @@ -485,10 +482,10 @@ impl CodeSplitter { cgi_ukey, chunk, compilation, - ); + )?; } - true + Ok(true) } #[instrument(skip_all)] @@ -509,7 +506,12 @@ impl CodeSplitter { ordinal_by_module.insert(*m, ordinal_by_module.len() as u64 + 1); } } - for chunk in compilation.build_chunk_graph_artifact.chunk_by_ukey.keys() { + for chunk in compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .keys() + { let mut mask = BigUint::from(0u32); for module_id in compilation .build_chunk_graph_artifact @@ -519,7 +521,11 @@ impl CodeSplitter { let module_ordinal = self.get_module_ordinal(*module_id); mask.set_bit(module_ordinal, true); } - self.mask_by_chunk.insert(*chunk, mask); + self.mask_by_chunk.insert( + &compilation.build_chunk_graph_artifact.chunk_graph.chunks, + *chunk, + mask, + ); } self.stat_invalidated_chunk_group = 0; diff --git a/crates/rspack_core/src/compilation/build_module_graph/module_executor/execute.rs b/crates/rspack_core/src/compilation/build_module_graph/module_executor/execute.rs index 4260750ee874..8deb4d3eb69b 100644 --- a/crates/rspack_core/src/compilation/build_module_graph/module_executor/execute.rs +++ b/crates/rspack_core/src/compilation/build_module_graph/module_executor/execute.rs @@ -10,10 +10,10 @@ use tokio::sync::oneshot::Sender; use super::context::{ExecutorTaskContext, ImportModuleMeta}; use crate::{ - Chunk, ChunkGraph, ChunkKind, ChunkUkey, CodeGenerationDataAssetInfo, CodeGenerationDataFilename, - CodeGenerationResult, CodeGenerationResultBuilder, Compilation, CompilationAsset, - CompilationAssets, EntryOptions, Entrypoint, ModuleCodeGenerationContext, ModuleType, PublicPath, - RuntimeSpec, SourceType, + ChunkGraph, ChunkKind, ChunkSet, ChunkUkey, CodeGenerationDataAssetInfo, + CodeGenerationDataFilename, CodeGenerationResult, CodeGenerationResultBuilder, Compilation, + CompilationAsset, CompilationAssets, EntryOptions, Entrypoint, ModuleCodeGenerationContext, + ModuleType, PublicPath, RuntimeSpec, SourceType, compilation::{ code_generation::code_generation_modules, create_module_hashes::create_module_hashes, @@ -330,7 +330,9 @@ impl Task for ExecuteTask { let mut chunk_graph = ChunkGraph::default(); - let mut chunk = Chunk::new(Some("build time chunk".into()), ChunkKind::Normal); + let chunk_ukey = + chunk_graph.create_chunk(Some("build time chunk".into()), ChunkKind::Normal)?; + let chunk = chunk_graph.chunks.expect_get_mut(&chunk_ukey); if let Some(name) = chunk.name() { let name = name.to_string(); @@ -357,14 +359,7 @@ impl Task for ExecuteTask { }), }); - // add chunk to this compilation - let chunk = compilation - .build_chunk_graph_artifact - .chunk_by_ukey - .add(chunk); - let chunk_ukey = chunk.ukey(); - - chunk_graph.connect_chunk_and_entry_module( + chunk_graph.topology.connect_chunk_and_entry_module( chunk.ukey(), entry_module_identifier, entrypoint.ukey, @@ -399,9 +394,12 @@ impl Task for ExecuteTask { let plugin_driver = compilation.plugin_driver.clone(); process_modules_runtime_requirements(&mut compilation, modules.clone(), plugin_driver.clone()) .await?; + let chunks = &compilation.build_chunk_graph_artifact.chunk_graph.chunks; + let mut runtime_requirement_chunks = ChunkSet::with_capacity(chunks.slot_count()); + runtime_requirement_chunks.insert(chunks, chunk_ukey); process_chunks_runtime_requirements( &mut compilation, - FxHashSet::from_iter([chunk_ukey]), + runtime_requirement_chunks, FxHashSet::from_iter([chunk_ukey]), plugin_driver, ) diff --git a/crates/rspack_core/src/compilation/chunk_ids/mod.rs b/crates/rspack_core/src/compilation/chunk_ids/mod.rs index e25684c23f31..bb86cafb182c 100644 --- a/crates/rspack_core/src/compilation/chunk_ids/mod.rs +++ b/crates/rspack_core/src/compilation/chunk_ids/mod.rs @@ -24,25 +24,14 @@ impl PassExt for ChunkIdsPass { compilation.named_chunk_ids_artifact.clear(); } - let mut diagnostics = vec![]; - let mut chunk_by_ukey = mem::take(&mut compilation.build_chunk_graph_artifact.chunk_by_ukey); - let mut named_chunk_ids_artifact = compilation.named_chunk_ids_artifact.steal(); compilation .plugin_driver .clone() .compilation_hooks .chunk_ids - .call( - compilation, - &mut chunk_by_ukey, - &mut named_chunk_ids_artifact, - &mut diagnostics, - ) + .call(compilation) .await .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.chunkIds"))?; - compilation.build_chunk_graph_artifact.chunk_by_ukey = chunk_by_ukey; - compilation.named_chunk_ids_artifact = named_chunk_ids_artifact.into(); - compilation.extend_diagnostics(diagnostics); Ok(()) } } diff --git a/crates/rspack_core/src/compilation/code_generation/mod.rs b/crates/rspack_core/src/compilation/code_generation/mod.rs index d35b7392d9ac..3119f5a3a37c 100644 --- a/crates/rspack_core/src/compilation/code_generation/mod.rs +++ b/crates/rspack_core/src/compilation/code_generation/mod.rs @@ -141,10 +141,7 @@ pub(crate) async fn code_generation_modules( let mut jobs = Vec::new(); for module in modules { let mut map: HashMap = HashMap::default(); - for runtime in chunk_graph.get_module_runtimes_iter( - module, - &compilation.build_chunk_graph_artifact.chunk_by_ukey, - ) { + for runtime in chunk_graph.get_module_runtimes_iter(module) { let hash = ChunkGraph::get_module_hash(compilation, module, runtime) .expect("should have cgm.hash in code generation"); let scope = compilation diff --git a/crates/rspack_core/src/compilation/create_chunk_assets/mod.rs b/crates/rspack_core/src/compilation/create_chunk_assets/mod.rs index cb953341a1ae..aa9511802421 100644 --- a/crates/rspack_core/src/compilation/create_chunk_assets/mod.rs +++ b/crates/rspack_core/src/compilation/create_chunk_assets/mod.rs @@ -86,7 +86,8 @@ pub async fn create_chunk_assets( compilation.chunk_render_artifact.retain(|chunk, _| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .contains(chunk) }); let chunks: FxHashSet = mutations @@ -101,13 +102,18 @@ pub async fn create_chunk_assets( logger.log(format!( "{} chunks are affected, {} in total", chunks.len(), - compilation.build_chunk_graph_artifact.chunk_by_ukey.len() + compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .len() )); chunks } else { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .keys() .copied() .collect() @@ -171,7 +177,8 @@ pub async fn create_chunk_assets( let filename = file_manifest.filename; let current_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&chunk_ukey); current_chunk.set_rendered(true); diff --git a/crates/rspack_core/src/compilation/create_hash/mod.rs b/crates/rspack_core/src/compilation/create_hash/mod.rs index a62c34f60904..7a57b7d25f85 100644 --- a/crates/rspack_core/src/compilation/create_hash/mod.rs +++ b/crates/rspack_core/src/compilation/create_hash/mod.rs @@ -3,7 +3,7 @@ use rspack_hash::RspackHasher; use rustc_hash::FxHashSet; use super::*; -use crate::{ModuleCodeGenerationContext, compilation::pass::PassExt, logger::Logger}; +use crate::{ChunkSet, ModuleCodeGenerationContext, compilation::pass::PassExt, logger::Logger}; pub struct ChunkHashResult { pub hash: RspackHashDigest, @@ -43,7 +43,8 @@ pub async fn create_hash( // dependent_full_hash hook to declare it. let mut full_hash_chunks: FxHashSet<_> = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .keys() .copied() .collect::>() @@ -87,7 +88,9 @@ pub async fn create_hash( compilation.chunk_hashes_artifact.clear(); } - let create_hash_chunks = if let Some(mutations) = compilation + let owner = &compilation.build_chunk_graph_artifact.chunk_graph.chunks; + let mut create_hash_chunks = ChunkSet::with_capacity(owner.slot_count()); + if let Some(mutations) = compilation .incremental .mutations_read(IncrementalPasses::CHUNKS_HASHES) && !compilation.chunk_hashes_artifact.is_empty() @@ -102,7 +105,8 @@ pub async fn create_hash( compilation.chunk_hashes_artifact.retain(|chunk, _| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .contains(chunk) }); let chunks = mutations.get_affected_chunks_with_chunk_graph(compilation); @@ -111,17 +115,20 @@ pub async fn create_hash( logger.log(format!( "{} chunks are affected, {} in total", chunks.len(), - compilation.build_chunk_graph_artifact.chunk_by_ukey.len(), + compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .len(), )); - chunks + for key in chunks.into_iter().filter(|key| owner.contains(key)) { + create_hash_chunks.insert(owner, key); + } } else { - compilation - .build_chunk_graph_artifact - .chunk_by_ukey - .keys() - .copied() - .collect() - }; + for key in owner.keys() { + create_hash_chunks.insert(owner, *key); + } + } let mut compilation_hasher = RspackHasher::from(&compilation.options.output); @@ -133,7 +140,8 @@ pub async fn create_hash( let (chunk_ukey, chunk_hash_result) = hash_result?; let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); let chunk_hashes_changed = chunk.set_hashes( &mut compilation.chunk_hashes_artifact, @@ -154,6 +162,7 @@ pub async fn create_hash( let other_chunks: Vec<_> = create_hash_chunks .iter() .filter(|key| !unordered_runtime_chunks.contains(key)) + .sorted_unstable() .collect(); // create hash for runtime modules in other chunks @@ -217,7 +226,8 @@ pub async fn create_hash( for runtime_chunk_ukey in runtime_chunk_keys { let runtime_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&runtime_chunk_ukey); let groups = runtime_chunk.get_all_referenced_async_entrypoints( &compilation.build_chunk_graph_artifact.chunk_group_by_ukey, @@ -309,7 +319,8 @@ pub async fn create_hash( .map(|(chunk_ukey, _)| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey) }) .collect(); @@ -365,7 +376,8 @@ pub async fn create_hash( process_chunk_hash(compilation, runtime_chunk_ukey, &plugin_driver).await?; let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&runtime_chunk_ukey); let chunk_hashes_changed = chunk.set_hashes( &mut compilation.chunk_hashes_artifact, @@ -383,7 +395,8 @@ pub async fn create_hash( // create full hash compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .values() .sorted_unstable_by_key(|chunk| chunk.ukey()) .for_each(|chunk| { @@ -421,7 +434,8 @@ pub async fn create_hash( } let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); let new_chunk_hash = { let chunk_hash = chunk @@ -527,7 +541,8 @@ async fn process_chunk_hash( let mut hasher = RspackHasher::from(&compilation.options.output); if let Some(chunk) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(&chunk_ukey) { chunk.update_hash(&mut hasher, compilation); diff --git a/crates/rspack_core/src/compilation/create_module_assets/mod.rs b/crates/rspack_core/src/compilation/create_module_assets/mod.rs index 7edbf445319d..2df0dd5d85a6 100644 --- a/crates/rspack_core/src/compilation/create_module_assets/mod.rs +++ b/crates/rspack_core/src/compilation/create_module_assets/mod.rs @@ -62,7 +62,8 @@ pub async fn create_module_assets( for (chunk, asset_name) in chunk_asset_map { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&chunk); chunk.add_auxiliary_file(asset_name); } diff --git a/crates/rspack_core/src/compilation/create_module_hashes/mod.rs b/crates/rspack_core/src/compilation/create_module_hashes/mod.rs index aefa04f4bf58..27fcdff3e25c 100644 --- a/crates/rspack_core/src/compilation/create_module_hashes/mod.rs +++ b/crates/rspack_core/src/compilation/create_module_hashes/mod.rs @@ -49,7 +49,7 @@ async fn create_module_hashes_pass_impl(compilation: &mut Compilation) -> Result let module_runtimes = compilation .build_chunk_graph_artifact .chunk_graph - .get_module_runtimes(*mi, &compilation.build_chunk_graph_artifact.chunk_by_ukey); + .get_module_runtimes(*mi); let module_runtime_keys = module_runtimes .values() .map(get_runtime_key) @@ -122,25 +122,22 @@ pub async fn create_module_hashes( ) -> Result<()> { let mg = compilation.get_module_graph(); let chunk_graph = &compilation.build_chunk_graph_artifact.chunk_graph; - let chunk_by_ukey = &compilation.build_chunk_graph_artifact.chunk_by_ukey; let compilation_ref = &*compilation; let results = rspack_parallel::scope::<_, Result<_>>(|token| { for module_identifier in modules { - let s = unsafe { token.used((compilation_ref, &mg, chunk_graph, chunk_by_ukey)) }; - s.spawn( - move |(compilation, mg, chunk_graph, chunk_by_ukey)| async move { - let mut hashes = RuntimeSpecMap::new(); - let module = mg - .module_by_identifier(&module_identifier) - .expect("should have module"); - for runtime in chunk_graph.get_module_runtimes_iter(module_identifier, chunk_by_ukey) { - let hash = module.get_runtime_hash(compilation, Some(runtime)).await?; - hashes.set(runtime.clone(), hash); - } - Ok((module_identifier, hashes)) - }, - ); + let s = unsafe { token.used((compilation_ref, &mg, chunk_graph)) }; + s.spawn(move |(compilation, mg, chunk_graph)| async move { + let mut hashes = RuntimeSpecMap::new(); + let module = mg + .module_by_identifier(&module_identifier) + .expect("should have module"); + for runtime in chunk_graph.get_module_runtimes_iter(module_identifier) { + let hash = module.get_runtime_hash(compilation, Some(runtime)).await?; + hashes.set(runtime.clone(), hash); + } + Ok((module_identifier, hashes)) + }); } }) .await diff --git a/crates/rspack_core/src/compilation/mod.rs b/crates/rspack_core/src/compilation/mod.rs index bebe04607939..36be1e9e5588 100644 --- a/crates/rspack_core/src/compilation/mod.rs +++ b/crates/rspack_core/src/compilation/mod.rs @@ -72,9 +72,9 @@ pub use self::{ use crate::{ AsyncModulesArtifact, BindingCell, BoxModule, BuildChunkGraphArtifact, CacheCount, CacheOptions, CgcRuntimeRequirementsArtifact, CgmHashArtifact, CgmRuntimeRequirementsArtifact, Chunk, - ChunkByUkey, ChunkContentHash, ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey, ChunkHashesArtifact, - ChunkKind, ChunkNamedIdArtifact, ChunkRenderArtifact, ChunkRenderCacheArtifact, - ChunkRenderResult, ChunkUkey, CircularModulesInfo, CodeGenerateCacheArtifact, CodeGenerationJob, + ChunkContentHash, ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey, ChunkHashesArtifact, + ChunkNamedIdArtifact, ChunkRenderArtifact, ChunkRenderCacheArtifact, ChunkRenderResult, + ChunkSlotMap, ChunkUkey, CircularModulesInfo, CodeGenerateCacheArtifact, CodeGenerationJob, CodeGenerationResult, CodeGenerationResultBuilder, CodeGenerationResults, CompilationLogger, CompilationLogging, CompilerOptions, CompilerPlatform, ConcatenationScope, DependenciesDiagnosticsArtifact, Dependency, DependencyId, DependencyRef, DependencyTemplate, @@ -129,7 +129,7 @@ define_hook!(CompilationReviveModules: Series(compilation: &Compilation, modules define_hook!(CompilationBeforeModuleIds: Series(compilation: &Compilation, modules: &IdentifierSet, preserved_module_ids: &mut ModuleIdsArtifact)); define_hook!(CompilationModuleIds: Series(compilation: &Compilation, module_ids: &mut ModuleIdsArtifact, preserved_module_ids: &ModuleIdsArtifact, diagnostics: &mut Vec)); define_hook!(CompilationRecordModules: Series(compilation: &Compilation, module_ids: &ModuleIdsArtifact)); -define_hook!(CompilationChunkIds: Series(compilation: &Compilation, chunk_by_ukey: &mut ChunkByUkey, named_chunk_ids_artifact: &mut ChunkNamedIdArtifact, diagnostics: &mut Vec)); +define_hook!(CompilationChunkIds: Series(compilation: &mut Compilation)); define_hook!(CompilationRuntimeModule: Series(compilation: &Compilation, module: &ModuleIdentifier, chunk: &ChunkUkey, runtime_modules: &mut IdentifierMap>)); define_hook!(CompilationAdditionalModuleRuntimeRequirements: Series(compilation: &Compilation, module_identifier: &ModuleIdentifier, runtime_requirements: &mut RuntimeGlobals),tracing=false); define_hook!(CompilationRuntimeRequirementInModule: SeriesBail(compilation: &Compilation, module_identifier: &ModuleIdentifier, all_runtime_requirements: &RuntimeGlobals, runtime_requirements: &RuntimeGlobals, runtime_requirements_mut: &mut RuntimeGlobals),tracing=false); @@ -889,7 +889,8 @@ impl Compilation { } self .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .iter_mut() .for_each(|(_, chunk)| { chunk.remove_file(filename); @@ -915,7 +916,8 @@ impl Compilation { self .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .iter_mut() .for_each(|(_, chunk)| { if chunk.remove_file(filename) { @@ -936,7 +938,8 @@ impl Compilation { pub fn par_rename_assets(&mut self, renames: Vec<(String, String)>) { self .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .values_mut() .par_bridge() .for_each(|chunk| { @@ -1057,31 +1060,6 @@ impl Compilation { Stats::new(StatsContext::new(self)) } - pub fn add_named_chunk( - name: String, - chunk_by_ukey: &mut ChunkByUkey, - named_chunks: &mut HashMap, - ) -> (ChunkUkey, bool) { - let existed_chunk_ukey = named_chunks.get(&name); - if let Some(chunk_ukey) = existed_chunk_ukey { - assert!(chunk_by_ukey.contains(chunk_ukey)); - (*chunk_ukey, false) - } else { - let chunk = Chunk::new(Some(name.clone()), ChunkKind::Normal); - let ukey = chunk.ukey(); - named_chunks.insert(name, ukey); - chunk_by_ukey.entry(ukey).or_insert_with(|| chunk); - (ukey, true) - } - } - - pub fn add_chunk(chunk_by_ukey: &mut ChunkByUkey) -> ChunkUkey { - let chunk = Chunk::new(None, ChunkKind::Normal); - let ukey = chunk.ukey(); - chunk_by_ukey.add(chunk); - ukey - } - pub async fn rebuild_module( &mut self, module_identifiers: IdentifierSet, @@ -1184,7 +1162,8 @@ impl Compilation { // add chunk runtime to prefix module identifier to avoid multiple entry runtime modules conflict let chunk = self .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let runtime_module_identifier = ModuleIdentifier::from(format!( "{}/{}", diff --git a/crates/rspack_core/src/compilation/runtime_requirements/mod.rs b/crates/rspack_core/src/compilation/runtime_requirements/mod.rs index 601530c847a4..6951d8b8812e 100644 --- a/crates/rspack_core/src/compilation/runtime_requirements/mod.rs +++ b/crates/rspack_core/src/compilation/runtime_requirements/mod.rs @@ -3,8 +3,8 @@ use rustc_hash::{FxHashMap, FxHashSet}; use super::*; use crate::{ - CodeGenerationRuntimeRequirementsWrite, RuntimeProxyMetadata, compilation::pass::PassExt, - logger::Logger, runtime_globals::BOOTSTRAP_RUNTIME_CONTEXT_GLOBALS, + ChunkMap, ChunkSet, CodeGenerationRuntimeRequirementsWrite, RuntimeProxyMetadata, + compilation::pass::PassExt, logger::Logger, runtime_globals::BOOTSTRAP_RUNTIME_CONTEXT_GLOBALS, runtime_mode::RuntimeMode as ExperimentRuntimeMode, }; @@ -80,7 +80,9 @@ async fn runtime_requirements_pass_impl(compilation: &mut Compilation) -> Result compilation.cgc_runtime_requirements_artifact.clear(); } - let process_runtime_requirements_chunks = if let Some(mutations) = compilation + let owner = &compilation.build_chunk_graph_artifact.chunk_graph.chunks; + let mut process_runtime_requirements_chunks = ChunkSet::with_capacity(owner.slot_count()); + if let Some(mutations) = compilation .incremental .mutations_read(IncrementalPasses::CHUNKS_RUNTIME_REQUIREMENTS) && !compilation.cgc_runtime_requirements_artifact.is_empty() @@ -110,24 +112,31 @@ async fn runtime_requirements_pass_impl(compilation: &mut Compilation) -> Result .retain(|chunk, _| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .contains(chunk) }); let logger = compilation.get_logger("rspack.incremental.chunksRuntimeRequirements"); logger.log(format!( "{} chunks are affected, {} in total", affected_chunks.len(), - compilation.build_chunk_graph_artifact.chunk_by_ukey.len() + compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .len() )); - affected_chunks + for key in affected_chunks + .into_iter() + .filter(|key| owner.contains(key)) + { + process_runtime_requirements_chunks.insert(owner, key); + } } else { - compilation - .build_chunk_graph_artifact - .chunk_by_ukey - .keys() - .copied() - .collect() - }; + for key in owner.keys() { + process_runtime_requirements_chunks.insert(owner, *key); + } + } process_chunks_runtime_requirements( compilation, process_runtime_requirements_chunks, @@ -211,10 +220,7 @@ pub async fn process_modules_runtime_requirements( let runtimes = compilation .build_chunk_graph_artifact .chunk_graph - .get_module_runtimes_iter( - module, - &compilation.build_chunk_graph_artifact.chunk_by_ukey, - ); + .get_module_runtimes_iter(module); for runtime in runtimes { let runtime_requirements = compilation .process_runtime_requirements_cache_artifact @@ -296,39 +302,40 @@ pub async fn process_modules_runtime_requirements( #[instrument(name = "Compilation:process_chunks_runtime_requirements", target=TRACING_BENCH_TARGET skip_all)] pub async fn process_chunks_runtime_requirements( compilation: &mut Compilation, - chunks: FxHashSet, + chunks: ChunkSet, entries: FxHashSet, plugin_driver: SharedPluginDriver, ) -> Result<()> { let logger = compilation.get_logger("rspack.Compilation"); let start = logger.time("runtime requirements.chunks"); - let chunk_requirements = chunks - .iter() - .chain(entries.iter()) - .par_bridge() - .map(|chunk_ukey| { - let mut set = RuntimeGlobals::default(); + let owner = &compilation.build_chunk_graph_artifact.chunk_graph.chunks; + let mut chunk_requirements = ChunkMap::with_capacity(owner.slot_count()); + for key in chunks.into_keys().chain(entries.iter().copied()) { + chunk_requirements.insert(owner, key, RuntimeGlobals::default()); + } + // Each current Chunk is computed once, even if it is in both input sets. + chunk_requirements + .par_iter_mut() + .for_each(|(chunk_ukey, set)| { + let chunk = owner.expect_get(chunk_ukey); for mid in compilation .build_chunk_graph_artifact .chunk_graph .get_chunk_modules_identifier(chunk_ukey) { - let chunk = compilation - .build_chunk_graph_artifact - .chunk_by_ukey - .expect_get(chunk_ukey); - if let Some(runtime_requirements) = + if let Some(requirements) = ChunkGraph::get_module_runtime_requirements(compilation, *mid, chunk.runtime()) { - set.insert(*runtime_requirements); + set.insert(*requirements); } } + }); - (*chunk_ukey, set) - }) - .collect::>(); - - for (chunk_ukey, mut set) in chunk_requirements { + // Hook order must not depend on reusable slot indices or parallel scheduling. + for (chunk_ukey, mut set) in chunk_requirements + .into_entries() + .sorted_unstable_by_key(|(key, _)| *key) + { let mut additional_runtime_modules = Vec::new(); plugin_driver .compilation_hooks @@ -407,7 +414,8 @@ pub async fn process_chunks_runtime_requirements( let entry = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&entry_ukey); for chunk_ukey in entry .get_all_referenced_chunks(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) @@ -525,13 +533,15 @@ pub async fn process_chunks_runtime_requirements( let metadata_chunk_ukeys = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .keys() .copied() .filter(|chunk_ukey| { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); chunk.has_runtime(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) || compilation @@ -544,7 +554,8 @@ pub async fn process_chunks_runtime_requirements( for chunk_ukey in metadata_chunk_ukeys { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); let owns_runtime = chunk.has_runtime(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey); @@ -559,7 +570,8 @@ pub async fn process_chunks_runtime_requirements( for referenced_chunk_ukey in referenced_chunks.iter() { let referenced_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(referenced_chunk_ukey); for mid in compilation .build_chunk_graph_artifact diff --git a/crates/rspack_core/src/compiler/rebuild.rs b/crates/rspack_core/src/compiler/rebuild.rs index ef6130701206..da6bfee26ff3 100644 --- a/crates/rspack_core/src/compiler/rebuild.rs +++ b/crates/rspack_core/src/compiler/rebuild.rs @@ -178,7 +178,8 @@ impl CompilationRecords { { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk); let chunk_id = chunk.id().expect("should have chunk_id").clone(); let hash = compilation @@ -215,7 +216,8 @@ impl CompilationRecords { .filter_map(|entry_ukey| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(&entry_ukey) }) .flat_map(|entry_chunk| entry_chunk.runtime().iter().copied()) @@ -225,7 +227,8 @@ impl CompilationRecords { fn record_chunks(compilation: &Compilation) -> ChunkIdMap<(RuntimeSpec, ModuleIdSet)> { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .values() .filter(|chunk| chunk.kind() != ChunkKind::HotUpdate) .map(|chunk| { diff --git a/crates/rspack_core/src/context_module.rs b/crates/rspack_core/src/context_module.rs index a11e9a75d59e..03065ff2c0fa 100644 --- a/crates/rspack_core/src/context_module.rs +++ b/crates/rspack_core/src/context_module.rs @@ -803,7 +803,8 @@ impl ContextModule { .map(|c| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(c) .id() .expect("should have chunk id in code generation") diff --git a/crates/rspack_core/src/incremental/mutations.rs b/crates/rspack_core/src/incremental/mutations.rs index 57da8635b80c..ab0097384f0e 100644 --- a/crates/rspack_core/src/incremental/mutations.rs +++ b/crates/rspack_core/src/incremental/mutations.rs @@ -174,7 +174,8 @@ impl Mutations { Mutation::ChunkSetId { chunk } => { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk); modules.extend( chunk diff --git a/crates/rspack_core/src/lib.rs b/crates/rspack_core/src/lib.rs index 291b32ec3e1e..7e08a3cc3740 100644 --- a/crates/rspack_core/src/lib.rs +++ b/crates/rspack_core/src/lib.rs @@ -321,86 +321,6 @@ pub type ModuleLayer = String; pub(crate) type SharedPluginDriver = Arc; -#[derive(Debug, Default, Clone)] -pub struct ChunkByUkey { - inner: FxHashMap, -} - -impl ChunkByUkey { - pub fn get(&self, ukey: &ChunkUkey) -> Option<&Chunk> { - self.inner.get(ukey) - } - - pub fn get_mut(&mut self, ukey: &ChunkUkey) -> Option<&mut Chunk> { - self.inner.get_mut(ukey) - } - - pub fn get_many_mut( - &mut self, - ukeys: [&ChunkUkey; N], - ) -> [Option<&mut Chunk>; N] { - self.inner.get_disjoint_mut(ukeys) - } - - pub fn expect_get(&self, ukey: &ChunkUkey) -> &Chunk { - self - .get(ukey) - .unwrap_or_else(|| panic!("Chunk({ukey:?}) not found in ChunkByUkey")) - } - - pub fn expect_get_mut(&mut self, ukey: &ChunkUkey) -> &mut Chunk { - self - .get_mut(ukey) - .unwrap_or_else(|| panic!("Chunk({ukey:?}) not found in ChunkByUkey")) - } - - pub fn add(&mut self, chunk: Chunk) -> &mut Chunk { - let ukey = chunk.ukey(); - debug_assert!(!self.inner.contains_key(&ukey)); - self.inner.entry(ukey).or_insert(chunk) - } - - pub fn remove(&mut self, ukey: &ChunkUkey) -> Option { - self.inner.remove(ukey) - } - - pub fn entry( - &mut self, - ukey: ChunkUkey, - ) -> std::collections::hash_map::Entry<'_, ChunkUkey, Chunk> { - self.inner.entry(ukey) - } - - pub fn contains(&self, ukey: &ChunkUkey) -> bool { - self.inner.contains_key(ukey) - } - - pub fn keys(&self) -> impl Iterator { - self.inner.keys() - } - - pub fn values(&self) -> impl Iterator { - self.inner.values() - } - - pub fn values_mut(&mut self) -> impl Iterator { - self.inner.values_mut() - } - - pub fn iter(&self) -> impl Iterator { - self.inner.iter() - } - - pub fn iter_mut(&mut self) -> impl Iterator { - self.inner.iter_mut() - } - - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> usize { - self.inner.len() - } -} - #[derive(Debug, Default, Clone)] pub struct ChunkGroupByUkey { inner: FxHashMap, diff --git a/crates/rspack_core/src/runtime_template.rs b/crates/rspack_core/src/runtime_template.rs index 0524f527ea78..67ed795e04d1 100644 --- a/crates/rspack_core/src/runtime_template.rs +++ b/crates/rspack_core/src/runtime_template.rs @@ -1089,7 +1089,8 @@ impl ModuleCodeTemplate { .map(|c| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(c) }) .filter(|c| { diff --git a/crates/rspack_core/src/stats/mod.rs b/crates/rspack_core/src/stats/mod.rs index e2b6d9190366..096032c1e492 100644 --- a/crates/rspack_core/src/stats/mod.rs +++ b/crates/rspack_core/src/stats/mod.rs @@ -305,7 +305,12 @@ impl Stats<'_> { let mut compilation_file_to_chunks: HashMap<&String, Vec<&Chunk>> = HashMap::default(); let mut compilation_file_to_auxiliary_chunks: HashMap<&String, Vec<&Chunk>> = HashMap::default(); - for chunk in self.build_chunk_graph_artifact().chunk_by_ukey.values() { + for chunk in self + .build_chunk_graph_artifact() + .chunk_graph + .chunks + .values() + { for file in chunk.files() { let chunks = compilation_file_to_chunks.entry(file).or_default(); chunks.push(chunk); @@ -603,7 +608,8 @@ impl Stats<'_> { let mut chunks: Vec = self .build_chunk_graph_artifact() - .chunk_by_ukey + .chunk_graph + .chunks .values() .par_bridge() .map(|c| -> Result<_> { @@ -665,7 +671,7 @@ impl Stats<'_> { get_chunk_relations( c, &build_chunk_graph_artifact.chunk_group_by_ukey, - &build_chunk_graph_artifact.chunk_by_ukey, + &build_chunk_graph_artifact.chunk_graph.chunks, ) }) .map_or((None, None, None), |(parents, children, siblings)| { @@ -678,7 +684,7 @@ impl Stats<'_> { c, order, &build_chunk_graph_artifact.chunk_group_by_ukey, - &build_chunk_graph_artifact.chunk_by_ukey, + &build_chunk_graph_artifact.chunk_graph.chunks, &build_chunk_graph_artifact.chunk_graph, module_graph, ) { @@ -794,7 +800,8 @@ impl Stats<'_> { .filter_map(|c| { self .build_chunk_graph_artifact() - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(c) .id() .map(|id| id.as_str()) @@ -805,7 +812,7 @@ impl Stats<'_> { .chunks .par_iter() .map(|c| { - let chunk = build_chunk_graph_artifact.chunk_by_ukey.expect_get(c); + let chunk = build_chunk_graph_artifact.chunk_graph.chunks.expect_get(c); chunk.files().par_iter().map(|file| StatsChunkGroupAsset { name: file.as_str(), size: get_asset_size(file, self.assets()), @@ -818,7 +825,7 @@ impl Stats<'_> { cg.chunks .par_iter() .map(|c| { - let chunk = build_chunk_graph_artifact.chunk_by_ukey.expect_get(c); + let chunk = build_chunk_graph_artifact.chunk_graph.chunks.expect_get(c); chunk .auxiliary_files() .par_iter() @@ -864,13 +871,13 @@ impl Stats<'_> { &ordered_children, &ChunkGroupOrderKey::Preload, &build_chunk_graph_artifact.chunk_group_by_ukey, - &build_chunk_graph_artifact.chunk_by_ukey, + &build_chunk_graph_artifact.chunk_graph.chunks, ), prefetch: get_chunk_group_ordered_child_assets( &ordered_children, &ChunkGroupOrderKey::Prefetch, &build_chunk_graph_artifact.chunk_group_by_ukey, - &build_chunk_graph_artifact.chunk_by_ukey, + &build_chunk_graph_artifact.chunk_graph.chunks, ), }, ) @@ -986,11 +993,12 @@ impl Stats<'_> { }) .unzip(); - let chunk = d.chunk.map(ChunkUkey::from).map(|key| { + let chunk = d.chunk.and_then(ChunkUkey::from_u64).and_then(|key| { self .build_chunk_graph_artifact() - .chunk_by_ukey - .expect_get(&key) + .chunk_graph + .chunks + .get(&key) }); let module_trace = get_module_trace( @@ -1069,11 +1077,12 @@ impl Stats<'_> { }) .unzip(); - let chunk = d.chunk.map(ChunkUkey::from).map(|key| { + let chunk = d.chunk.and_then(ChunkUkey::from_u64).and_then(|key| { self .build_chunk_graph_artifact() - .chunk_by_ukey - .expect_get(&key) + .chunk_graph + .chunks + .get(&key) }); let module_trace = get_module_trace( @@ -1316,7 +1325,8 @@ impl Stats<'_> { .filter_map(|k| { self .build_chunk_graph_artifact() - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(k) .id() .map(|id| id.as_str()) @@ -1615,7 +1625,8 @@ impl Stats<'_> { .filter_map(|k| { self .build_chunk_graph_artifact() - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(k) .id() .map(|id| id.as_str()) @@ -1740,11 +1751,12 @@ pub fn create_stats_errors<'a>( }) .unzip(); - let chunk = d.chunk.map(ChunkUkey::from).map(|key| { + let chunk = d.chunk.and_then(ChunkUkey::from_u64).and_then(|key| { compilation .build_chunk_graph_artifact - .chunk_by_ukey - .expect_get(&key) + .chunk_graph + .chunks + .get(&key) }); let module_trace = get_module_trace( diff --git a/crates/rspack_core/src/stats/utils.rs b/crates/rspack_core/src/stats/utils.rs index 54b41180aa3a..937081731d20 100644 --- a/crates/rspack_core/src/stats/utils.rs +++ b/crates/rspack_core/src/stats/utils.rs @@ -11,8 +11,8 @@ use super::{ StatsModule, StatsModuleTrace, }; use crate::{ - BoxRuntimeModule, Chunk, ChunkByUkey, ChunkGraph, ChunkGroup, ChunkGroupByUkey, - ChunkGroupOrderKey, ChunkGroupUkey, CompilationAssets, Context, ModuleGraph, ModuleId, + BoxRuntimeModule, Chunk, ChunkGraph, ChunkGroup, ChunkGroupByUkey, ChunkGroupOrderKey, + ChunkGroupUkey, ChunkSlotMap, CompilationAssets, Context, ModuleGraph, ModuleId, ModuleIdsArtifact, ModuleRef, SourceType, compare_chunks_iterables, rspack_sources::BoxSource, }; @@ -81,7 +81,7 @@ pub fn get_chunk_group_ordered_child_assets<'a>( ordered_children: &HashMap>, order_key: &ChunkGroupOrderKey, chunk_group_by_ukey: &ChunkGroupByUkey, - chunk_by_ukey: &'a ChunkByUkey, + chunk_by_ukey: &'a ChunkSlotMap, ) -> Vec<&'a str> { ordered_children .get(order_key) @@ -168,7 +168,7 @@ pub fn get_chunk_child_ids_by_order( chunk: &Chunk, order_key: &ChunkGroupOrderKey, chunk_group_by_ukey: &ChunkGroupByUkey, - chunk_by_ukey: &ChunkByUkey, + chunk_by_ukey: &ChunkSlotMap, chunk_graph: &ChunkGraph, module_graph: &ModuleGraph, ) -> Option> { @@ -226,7 +226,7 @@ pub fn get_chunk_child_ids_by_order( pub fn get_chunk_relations<'a>( chunk: &Chunk, chunk_group_by_ukey: &'a ChunkGroupByUkey, - chunk_by_ukey: &'a ChunkByUkey, + chunk_by_ukey: &'a ChunkSlotMap, ) -> (Vec<&'a str>, Vec<&'a str>, Vec<&'a str>) { let mut parents = HashSet::default(); let mut children = HashSet::default(); diff --git a/crates/rspack_core/src/ukey.rs b/crates/rspack_core/src/ukey.rs index f8c315bd50ae..e741dc012fd9 100644 --- a/crates/rspack_core/src/ukey.rs +++ b/crates/rspack_core/src/ukey.rs @@ -1,38 +1,58 @@ -use std::sync::atomic::AtomicU32; +use std::{num::NonZeroU32, sync::atomic::AtomicU32}; #[cfg(allocative)] use rspack_util::allocative; -use crate::{Chunk, ChunkGroup}; +use crate::ChunkGroup; -static NEXT_CHUNK_UKEY: AtomicU32 = AtomicU32::new(0); +// This allocator deliberately lives outside graph snapshots. Cloned graphs must +// never allocate the same identity when they reuse the same local slot. +static NEXT_CHUNK_UKEY: AtomicU32 = AtomicU32::new(1); +/// An in-process Chunk identity and its graph-local storage slot. +/// Ordering preserves allocation order, independently of slot reuse. #[rspack_cacheable::cacheable] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct ChunkUkey(u32, std::marker::PhantomData); - -impl Default for ChunkUkey { - fn default() -> Self { - Self::new() - } +pub struct ChunkUkey { + identity: NonZeroU32, + index: u32, } impl ChunkUkey { - pub fn new() -> Self { - Self( - NEXT_CHUNK_UKEY.fetch_add(1, std::sync::atomic::Ordering::Relaxed), - std::marker::PhantomData, - ) + pub(crate) fn allocate(index: u32) -> rspack_error::Result { + let identity = NEXT_CHUNK_UKEY + .fetch_update( + std::sync::atomic::Ordering::Relaxed, + std::sync::atomic::Ordering::Relaxed, + |value| value.checked_add(1), + ) + .map_err(|_| rspack_error::error!("Chunk identity space exhausted"))?; + Ok(Self { + identity: NonZeroU32::new(identity).expect("Chunk identities start at one"), + index, + }) } + pub(crate) fn index(self) -> usize { + self.index as usize + } + + /// Legacy identity projection for diagnostic tooling; never a storage index. pub fn as_u32(&self) -> u32 { - self.0 + self.identity.get() } -} -impl From for ChunkUkey { - fn from(value: u32) -> Self { - Self(value, std::marker::PhantomData) + /// Lossless process-local representation. Not a persistent cache identity. + pub fn as_u64(self) -> u64 { + ((self.identity.get() as u64) << 32) | self.index as u64 + } + + /// Decode a process-local diagnostic reference, rejecting the vacant identity. + pub fn from_u64(value: u64) -> Option { + Some(Self { + identity: NonZeroU32::new((value >> 32) as u32)?, + index: value as u32, + }) } } diff --git a/crates/rspack_core_test/Cargo.toml b/crates/rspack_core_test/Cargo.toml new file mode 100644 index 000000000000..d8fbe38e7b19 --- /dev/null +++ b/crates/rspack_core_test/Cargo.toml @@ -0,0 +1,15 @@ +[package] +edition.workspace = true +license.workspace = true +name = "rspack_core_test" +publish = false +version.workspace = true + +[dependencies] +rspack_cacheable = { workspace = true } +rspack_collections = { workspace = true } +rspack_core = { workspace = true } +rspack_error = { workspace = true } + +[lints] +workspace = true diff --git a/crates/rspack_core_test/src/lib.rs b/crates/rspack_core_test/src/lib.rs new file mode 100644 index 000000000000..b67b27e8289f --- /dev/null +++ b/crates/rspack_core_test/src/lib.rs @@ -0,0 +1 @@ +//! Contract tests for graph ownership, snapshots and process-local Chunk keys. diff --git a/crates/rspack_core_test/tests/chunk_graph.rs b/crates/rspack_core_test/tests/chunk_graph.rs new file mode 100644 index 000000000000..96412f39af2f --- /dev/null +++ b/crates/rspack_core_test/tests/chunk_graph.rs @@ -0,0 +1,206 @@ +use std::collections::{HashMap, HashSet}; + +use rspack_core::{BuildChunkGraphArtifact, ChunkGraph, ChunkKind, ChunkUkey}; + +fn create(graph: &mut ChunkGraph) -> ChunkUkey { + graph + .create_chunk(None, ChunkKind::Normal) + .expect("allocate Chunk") +} + +#[test] +fn recycled_slot_rejects_old_identity() { + let mut artifact = BuildChunkGraphArtifact::default(); + let old = create(&mut artifact.chunk_graph); + assert!(artifact.remove_chunk(&old).is_some()); + assert!(artifact.remove_chunk(&old).is_none()); + let current = create(&mut artifact.chunk_graph); + assert_eq!(old.as_u64() as u32, current.as_u64() as u32); + assert_ne!(old, current); + assert!(artifact.chunk_graph.chunks.get(&old).is_none()); + assert!(artifact.chunk_graph.chunks.get_mut(&old).is_none()); + assert!(artifact.remove_chunk(&old).is_none()); + assert!(artifact.chunk_graph.chunks.contains(¤t)); + assert_eq!(artifact.chunk_graph.chunks.len(), 1); + let next = create(&mut artifact.chunk_graph); + assert_ne!(next.as_u64() as u32, current.as_u64() as u32); +} + +#[test] +fn snapshot_branches_cannot_alias_cached_values() { + let mut artifact = BuildChunkGraphArtifact::default(); + let original = create(&mut artifact.chunk_graph); + let snapshot = artifact.chunk_graph.clone(); + artifact.remove_chunk(&original); + let previous = create(&mut artifact.chunk_graph); + let cache = HashMap::from([(previous, "previous optimization")]); + let history = HashSet::from([original, previous]); + + artifact.chunk_graph.clone_from(&snapshot); + assert!(artifact.chunk_graph.chunks.contains(&original)); + artifact.remove_chunk(&original); + let current = create(&mut artifact.chunk_graph); + assert_eq!(previous.as_u64() as u32, current.as_u64() as u32); + assert_ne!(previous, current); + assert!(!cache.contains_key(¤t)); + assert_eq!(history.len(), 2); + assert!(artifact.remove_chunk(&previous).is_none()); + assert!(snapshot.chunks.contains(&original)); +} + +#[test] +fn independently_created_graphs_have_distinct_keys() { + let mut a = ChunkGraph::default(); + let mut b = ChunkGraph::default(); + let a_key = create(&mut a); + let b_key = create(&mut b); + assert_eq!(a_key.as_u64() as u32, 0); + assert_eq!(b_key.as_u64() as u32, 0); + assert_ne!(a_key, b_key); + assert_ne!(a_key.as_u32(), 0); + assert_ne!(b_key.as_u32(), 0); + assert!(a.chunks.get(&b_key).is_none()); + assert!(b.chunks.get_mut(&a_key).is_none()); + assert_eq!(std::mem::size_of::(), 8); + assert_eq!(ChunkUkey::from_u64(a_key.as_u64()), Some(a_key)); +} + +#[test] +fn slot_layout_uses_the_identity_niche() { + fn check() { + assert_eq!( + std::mem::size_of::>(), + std::mem::size_of::<(ChunkUkey, T)>() + ); + } + check::<()>(); + check::(); + check::<[u8; 64]>(); + check::(); + check::(); + assert!(ChunkUkey::from_u64(0).is_none()); + assert!(ChunkUkey::from_u64(u32::MAX as u64).is_none()); +} + +#[test] +fn snapshots_preserve_free_slots_and_append_after_reuse() { + let mut artifact = BuildChunkGraphArtifact::default(); + let original = (0..8) + .map(|_| create(&mut artifact.chunk_graph)) + .collect::>(); + for index in [2, 5, 0] { + assert!(artifact.remove_chunk(&original[index]).is_some()); + } + assert!(artifact.remove_chunk(&original[5]).is_none()); + let snapshot = artifact.chunk_graph.clone(); + let mut identities = HashSet::new(); + for _ in 0..3 { + artifact.chunk_graph.clone_from(&snapshot); + for index in [0, 5, 2, 8] { + let key = create(&mut artifact.chunk_graph); + assert_eq!(key.as_u64() as u32, index); + assert_ne!(key.as_u32(), 0); + assert!(identities.insert(key)); + assert_eq!(artifact.chunk_graph.get_number_of_chunk_modules(&key), 0); + assert!(ChunkUkey::from_u64(index as u64).is_none()); + } + assert_eq!(artifact.chunk_graph.chunks.len(), 9); + assert_eq!(artifact.chunk_graph.chunks.values().count(), 9); + for index in [2, 5, 0] { + assert!(artifact.remove_chunk(&original[index]).is_none()); + } + } +} + +#[test] +fn removal_cleans_named_chunks_and_module_relationships() { + let mut artifact = BuildChunkGraphArtifact::default(); + let (key, created) = artifact + .add_named_chunk("entry".into()) + .expect("named Chunk"); + assert!(created); + assert_eq!( + artifact + .add_named_chunk("entry".into()) + .expect("existing Chunk"), + (key, false) + ); + let module = rspack_collections::Identifier::from("module"); + artifact.chunk_graph.connect_chunk_and_module(key, module); + artifact + .chunk_graph + .connect_chunk_and_runtime_module(key, module); + artifact.remove_chunk(&key); + assert!(artifact.named_chunks.is_empty()); + assert!(artifact.chunk_graph.get_module_chunks(module).is_empty()); + let (next, _) = artifact + .add_named_chunk("entry".into()) + .expect("replacement Chunk"); + assert_ne!(key, next); + assert_eq!(artifact.chunk_graph.get_number_of_chunk_modules(&next), 0); +} + +#[test] +fn repeated_snapshot_restore_keeps_storage_bounded() { + let mut artifact = BuildChunkGraphArtifact::default(); + let key = create(&mut artifact.chunk_graph); + artifact.remove_chunk(&key); + let snapshot = artifact.chunk_graph.clone(); + for _ in 0..1000 { + artifact.chunk_graph.clone_from(&snapshot); + let key = create(&mut artifact.chunk_graph); + assert_eq!(key.as_u64() as u32, 0); + assert_eq!(artifact.chunk_graph.chunks.values().count(), 1); + artifact.remove_chunk(&key); + assert_eq!(artifact.chunk_graph.chunks.keys().count(), 0); + } +} + +#[test] +fn diagnostics_cannot_persist_process_local_chunk_keys() { + let mut diagnostic = rspack_error::Diagnostic::warn("test".into(), "message".into()); + assert!(rspack_cacheable::to_bytes(&diagnostic, &()).is_ok()); + let key = create(&mut ChunkGraph::default()); + diagnostic.chunk = Some(key.as_u64()); + assert!(matches!( + rspack_cacheable::to_bytes(&diagnostic, &()), + Err(rspack_cacheable::Error::UnsupportedField) + )); +} + +#[test] +fn removing_entry_chunk_cleans_both_sides_of_the_group() { + let mut artifact = BuildChunkGraphArtifact::default(); + let key = create(&mut artifact.chunk_graph); + let module = rspack_collections::Identifier::from("entry-module"); + let mut group = rspack_core::ChunkGroup::default(); + group.set_entrypoint_chunk(key); + assert!(group.is_entrypoint_chunk(&key)); + let group_key = group.ukey(); + group.chunks.push(key); + artifact + .chunk_graph + .chunks + .expect_get_mut(&key) + .add_group(group_key); + artifact.chunk_group_by_ukey.add(group); + artifact + .chunk_graph + .connect_chunk_and_entry_module(key, module, group_key); + assert!(artifact.chunk_graph.is_entry_module(&module)); + artifact.remove_chunk(&key); + assert!(!artifact.chunk_graph.is_entry_module(&module)); + assert!( + !artifact + .chunk_group_by_ukey + .expect_get(&group_key) + .is_entrypoint_chunk(&key) + ); + assert!( + artifact + .chunk_group_by_ukey + .expect_get(&group_key) + .chunks + .is_empty() + ); +} diff --git a/crates/rspack_core_test/tests/chunk_map.rs b/crates/rspack_core_test/tests/chunk_map.rs new file mode 100644 index 000000000000..50d8a73da5d9 --- /dev/null +++ b/crates/rspack_core_test/tests/chunk_map.rs @@ -0,0 +1,155 @@ +use std::{ + cell::Cell, + panic::{AssertUnwindSafe, catch_unwind}, + rc::Rc, +}; + +use rspack_core::{BuildChunkGraphArtifact, ChunkKind, ChunkMap, ChunkSet}; + +#[test] +fn set_retains_slot_indices_and_rejects_stale_removal() { + let mut artifact = BuildChunkGraphArtifact::default(); + let keys: Vec<_> = (0..8) + .map(|_| { + artifact + .chunk_graph + .create_chunk(None, ChunkKind::Normal) + .unwrap() + }) + .collect(); + let mut set = ChunkSet::with_capacity(artifact.chunk_graph.chunks.slot_count()); + for key in [keys[7], keys[0], keys[3]] { + assert!(set.insert(&artifact.chunk_graph.chunks, key)); + assert!(!set.insert(&artifact.chunk_graph.chunks, key)); + } + assert_eq!(set.len(), 3); + set.retain(|key| *key != keys[3]); + assert!(!set.contains(&keys[3])); + assert!(set.contains(&keys[7])); + assert_eq!(set.iter().count(), 2); + + artifact.remove_chunk(&keys[0]); + let replacement = artifact + .chunk_graph + .create_chunk(None, ChunkKind::Normal) + .unwrap(); + assert!(set.insert(&artifact.chunk_graph.chunks, replacement)); + assert!(!set.contains(&keys[0])); + assert!(!set.remove(&keys[0])); + assert!(set.contains(&replacement)); + assert_eq!(set.len(), 2); + assert!(set.remove(&replacement)); + assert_eq!(set.into_keys().collect::>(), vec![keys[7]]); +} + +#[test] +fn sparse_insertion_reuse_and_restored_identity() { + let mut artifact = BuildChunkGraphArtifact::default(); + let keys: Vec<_> = (0..32) + .map(|_| { + artifact + .chunk_graph + .create_chunk(None, ChunkKind::Normal) + .unwrap() + }) + .collect(); + let snapshot = artifact.chunk_graph.clone(); + let mut map = ChunkMap::with_capacity(artifact.chunk_graph.chunks.slot_count()); + assert_eq!(map.slot_count(), 0); // Reserving does not initialize slots. + assert!(map.is_empty()); + assert_eq!(map.insert(&artifact.chunk_graph.chunks, keys[31], 31), None); + assert_eq!(map.slot_count(), 32); + assert_eq!(map.len(), 1); + assert_eq!(map.get(&keys[0]), None); + map.insert(&artifact.chunk_graph.chunks, keys[0], 0); + assert_eq!( + map.insert(&artifact.chunk_graph.chunks, keys[31], 32), + Some(31) + ); + assert_eq!( + map.keys().copied().collect::>(), + vec![keys[0], keys[31]] + ); + + artifact.remove_chunk(&keys[0]); + let replacement = artifact + .chunk_graph + .create_chunk(None, ChunkKind::Normal) + .unwrap(); + assert_eq!( + map.insert(&artifact.chunk_graph.chunks, replacement, 100), + None + ); + assert_eq!(map.get(&keys[0]), None); + assert_eq!(map.remove(&keys[0]), None); + assert_eq!(map.len(), 2); + // Membership, not identity ordering, decides whether restored keys are valid. + artifact.chunk_graph = snapshot; + assert_eq!(map.insert(&artifact.chunk_graph.chunks, keys[0], 200), None); + assert_eq!(map.get(&replacement), None); + assert_eq!(map.get(&keys[0]), Some(&200)); + assert_eq!(map.len(), 2); + artifact.remove_chunk(&keys[0]); + assert_eq!(map.remove(&keys[0]), Some(200)); + assert_eq!(map.remove(&keys[0]), None); +} + +#[test] +fn stale_and_foreign_insertions_cannot_evict_live_values() { + let mut artifact = BuildChunkGraphArtifact::default(); + let old = artifact + .chunk_graph + .create_chunk(None, ChunkKind::Normal) + .unwrap(); + artifact.remove_chunk(&old); + let live = artifact + .chunk_graph + .create_chunk(None, ChunkKind::Normal) + .unwrap(); + let mut other = rspack_core::ChunkGraph::default(); + let foreign = other.create_chunk(None, ChunkKind::Normal).unwrap(); + let mut map = ChunkMap::with_capacity(1); + map.insert(&artifact.chunk_graph.chunks, live, 42); + for invalid in [old, foreign] { + assert!( + catch_unwind(AssertUnwindSafe(|| { + map.insert(&artifact.chunk_graph.chunks, invalid, 0); + })) + .is_err() + ); + assert!( + catch_unwind(AssertUnwindSafe(|| { + map.get_or_insert_default(&artifact.chunk_graph.chunks, invalid); + })) + .is_err() + ); + assert_eq!(map.get(&live), Some(&42)); + assert_eq!(map.len(), 1); + } +} + +#[test] +fn payloads_without_default_are_dropped_exactly_once() { + #[derive(Clone)] + struct Payload(Rc>); + impl Drop for Payload { + fn drop(&mut self) { + self.0.set(self.0.get() + 1); + } + } + let drops = Rc::new(Cell::new(0)); + let mut graph = rspack_core::ChunkGraph::default(); + let key = graph.create_chunk(None, ChunkKind::Normal).unwrap(); + let mut map = ChunkMap::with_capacity(16); + map.insert(&graph.chunks, key, Payload(drops.clone())); + let snapshot = (*map).clone(); + let removed = map.remove(&key).unwrap(); + assert_eq!(drops.get(), 0); + drop(removed); + assert_eq!(drops.get(), 1); + map.insert(&graph.chunks, key, Payload(drops.clone())); + drop(map); + assert_eq!(drops.get(), 2); + drop(snapshot); + assert_eq!(drops.get(), 3); +} diff --git a/crates/rspack_error/src/diagnostic.rs b/crates/rspack_error/src/diagnostic.rs index 65e4ff062b84..281a73d10993 100644 --- a/crates/rspack_error/src/diagnostic.rs +++ b/crates/rspack_error/src/diagnostic.rs @@ -1,6 +1,6 @@ use rspack_cacheable::{ cacheable, - with::{AsOption, AsPreset}, + with::{AsOption, AsPreset, Unsupported}, }; use rspack_collections::Identifier; use rspack_location::DependencyLocation; @@ -19,7 +19,8 @@ pub struct Diagnostic { pub loc: Option, #[cacheable(with=AsOption)] pub file: Option, - pub chunk: Option, + #[cacheable(with=AsOption)] + pub chunk: Option, } impl std::ops::Deref for Diagnostic { diff --git a/crates/rspack_ids/src/compact_hashed_chunk_ids_plugin.rs b/crates/rspack_ids/src/compact_hashed_chunk_ids_plugin.rs index 28a40b9c1c05..bc462afcaaff 100644 --- a/crates/rspack_ids/src/compact_hashed_chunk_ids_plugin.rs +++ b/crates/rspack_ids/src/compact_hashed_chunk_ids_plugin.rs @@ -1,9 +1,6 @@ use rayon::prelude::*; -use rspack_core::{ - ChunkByUkey, ChunkNamedIdArtifact, Compilation, CompilationChunkIds, Plugin, - incremental::IncrementalPasses, -}; -use rspack_error::{Diagnostic, Result, error}; +use rspack_core::{Compilation, CompilationChunkIds, Plugin, incremental::IncrementalPasses}; +use rspack_error::{Result, error}; use rspack_hook::{plugin, plugin_hook}; use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; @@ -41,20 +38,14 @@ impl CompactHashedChunkIdsPlugin { } #[plugin_hook(CompilationChunkIds for CompactHashedChunkIdsPlugin)] -async fn chunk_ids( - &self, - compilation: &Compilation, - chunk_by_ukey: &mut ChunkByUkey, - _named_chunk_ids_artifact: &mut ChunkNamedIdArtifact, - diagnostics: &mut Vec, -) -> Result<()> { +async fn chunk_ids(&self, compilation: &mut Compilation) -> Result<()> { if let Some(diagnostic) = compilation.incremental.disable_passes( IncrementalPasses::CHUNK_IDS | IncrementalPasses::MODULES_HASHES, "CompactHashedChunkIdsPlugin (optimization.chunkIds = \"compact-hashed\")", "it requires calculating the id of all the chunks, which is a global effect", ) && let Some(diagnostic) = diagnostic { - diagnostics.push(diagnostic); + compilation.extend_diagnostics([diagnostic]); } validate_min_length( @@ -65,6 +56,7 @@ async fn chunk_ids( // [name] falls back to the chunk id for unnamed chunks. Reserve names as well as // preassigned ids to avoid filename collisions, including on case-insensitive file systems. + let chunk_by_ukey = &compilation.build_chunk_graph_artifact.chunk_graph.chunks; let used_ids = get_used_chunk_ids(chunk_by_ukey) .into_iter() .chain( @@ -132,7 +124,12 @@ async fn chunk_ids( } for (chunk_ukey, id) in chunk_key_to_id { - chunk_by_ukey.expect_get_mut(&chunk_ukey).set_id(id); + compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .expect_get_mut(&chunk_ukey) + .set_id(id); } Ok(()) diff --git a/crates/rspack_ids/src/deterministic_chunk_ids_plugin.rs b/crates/rspack_ids/src/deterministic_chunk_ids_plugin.rs index 53f11a71f47c..052f7a94d3f9 100644 --- a/crates/rspack_ids/src/deterministic_chunk_ids_plugin.rs +++ b/crates/rspack_ids/src/deterministic_chunk_ids_plugin.rs @@ -1,8 +1,6 @@ use rayon::prelude::*; -use rspack_core::{ - ChunkByUkey, ChunkNamedIdArtifact, CompilationChunkIds, Plugin, incremental::IncrementalPasses, -}; -use rspack_error::{Diagnostic, Result}; +use rspack_core::{CompilationChunkIds, Plugin, incremental::IncrementalPasses}; +use rspack_error::Result; use rspack_hook::{plugin, plugin_hook}; use rustc_hash::{FxBuildHasher, FxHashMap}; @@ -21,22 +19,17 @@ pub struct DeterministicChunkIdsPlugin { impl DeterministicChunkIdsPlugin {} #[plugin_hook(CompilationChunkIds for DeterministicChunkIdsPlugin)] -async fn chunk_ids( - &self, - compilation: &rspack_core::Compilation, - chunk_by_ukey: &mut ChunkByUkey, - _named_chunk_ids_artifact: &mut ChunkNamedIdArtifact, - diagnostics: &mut Vec, -) -> rspack_error::Result<()> { +async fn chunk_ids(&self, compilation: &mut rspack_core::Compilation) -> rspack_error::Result<()> { if let Some(diagnostic) = compilation.incremental.disable_passes( IncrementalPasses::CHUNK_IDS | IncrementalPasses::MODULES_HASHES, "DeterministicChunkIdsPlugin (optimization.chunkIds = \"deterministic\")", "it requires calculating the id of all the chunks, which is a global effect", ) && let Some(diagnostic) = diagnostic { - diagnostics.push(diagnostic); + compilation.extend_diagnostics([diagnostic]); } + let chunk_by_ukey = &compilation.build_chunk_graph_artifact.chunk_graph.chunks; let mut used_ids = get_used_chunk_ids(chunk_by_ukey); let used_ids_len = used_ids.len(); @@ -117,7 +110,11 @@ async fn chunk_ids( ); for (chunk_ukey, id) in chunk_key_to_id { - let chunk = chunk_by_ukey.expect_get_mut(&chunk_ukey); + let chunk = compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .expect_get_mut(&chunk_ukey); chunk.set_id(id.to_string()); } diff --git a/crates/rspack_ids/src/id_helpers.rs b/crates/rspack_ids/src/id_helpers.rs index c2887ca6b93e..1e411fceea5a 100644 --- a/crates/rspack_ids/src/id_helpers.rs +++ b/crates/rspack_ids/src/id_helpers.rs @@ -12,10 +12,10 @@ use itertools::{ }; use rspack_collections::Identifier; use rspack_core::{ - Chunk, ChunkByUkey, ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey, ChunkKind, - ChunkNamedIdArtifact, ChunkUkey, Compilation, CompilerId, DependencyLocation, - ExportsInfoArtifact, Module, ModuleGraph, ModuleGraphCacheArtifact, ModuleIdentifier, - ModuleIdsArtifact, ModuleRef, SideEffectsStateArtifact, compare_runtime, + Chunk, ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey, ChunkKind, ChunkNamedIdArtifact, + ChunkSlotMap, ChunkUkey, Compilation, CompilerId, DependencyLocation, ExportsInfoArtifact, + Module, ModuleGraph, ModuleGraphCacheArtifact, ModuleIdentifier, ModuleIdsArtifact, ModuleRef, + SideEffectsStateArtifact, compare_runtime, }; use rspack_error::{Result, error}; use rspack_util::{ @@ -317,7 +317,7 @@ mod tests { use std::cmp::Ordering; use rspack_core::{ - Chunk, ChunkGraph, ChunkGroup, ChunkGroupByUkey, ChunkKind, ModuleIdentifier, ModuleIdsArtifact, + ChunkGraph, ChunkGroup, ChunkGroupByUkey, ChunkKind, ModuleIdentifier, ModuleIdsArtifact, }; use rustc_hash::FxHashMap; @@ -407,8 +407,18 @@ mod tests { let mut chunk_group_by_ukey = ChunkGroupByUkey::default(); let mut cache = NaturalChunkCompareCache::default(); - let chunk_a = Chunk::new(None, ChunkKind::Normal); - let chunk_b = Chunk::new(None, ChunkKind::Normal); + let chunk_a = { + let key = chunk_graph + .create_chunk(None, ChunkKind::Normal) + .expect("test Chunk allocation"); + chunk_graph.chunks.expect_get(&key).clone() + }; + let chunk_b = { + let key = chunk_graph + .create_chunk(None, ChunkKind::Normal) + .expect("test Chunk allocation"); + chunk_graph.chunks.expect_get(&key).clone() + }; let module_a: ModuleIdentifier = "module-a".into(); let module_b: ModuleIdentifier = "module-b".into(); @@ -429,8 +439,18 @@ mod tests { Ordering::Less ); - let mut chunk_c = Chunk::new(None, ChunkKind::Normal); - let mut chunk_d = Chunk::new(None, ChunkKind::Normal); + let mut chunk_c = { + let key = chunk_graph + .create_chunk(None, ChunkKind::Normal) + .expect("test Chunk allocation"); + chunk_graph.chunks.expect_get(&key).clone() + }; + let mut chunk_d = { + let key = chunk_graph + .create_chunk(None, ChunkKind::Normal) + .expect("test Chunk allocation"); + chunk_graph.chunks.expect_get(&key).clone() + }; let shared_module: ModuleIdentifier = "shared-module".into(); ChunkGraph::set_module_id(&mut module_ids, shared_module, "shared".into()); chunk_graph.connect_chunk_and_module(chunk_c.ukey(), shared_module); @@ -461,7 +481,7 @@ mod tests { #[allow(clippy::too_many_arguments)] pub fn get_short_chunk_name( chunk: &Chunk, - chunk_graph: &ChunkGraph, + chunk_graph: &rspack_core::ChunkGraphTopology, context: &str, delimiter: &str, module_graph: &ModuleGraph, @@ -528,7 +548,7 @@ pub fn shorten_long_string(string: String, delimiter: &str) -> String { #[allow(clippy::too_many_arguments)] pub fn get_long_chunk_name( chunk: &Chunk, - chunk_graph: &ChunkGraph, + chunk_graph: &rspack_core::ChunkGraphTopology, context: &str, delimiter: &str, module_graph: &ModuleGraph, @@ -581,7 +601,7 @@ pub fn get_long_chunk_name( #[allow(clippy::too_many_arguments)] pub fn get_full_chunk_name( chunk: &Chunk, - chunk_graph: &ChunkGraph, + chunk_graph: &rspack_core::ChunkGraphTopology, chunk_group_by_ukey: &ChunkGroupByUkey, module_graph: &ModuleGraph, module_graph_cache: &ModuleGraphCacheArtifact, @@ -669,7 +689,7 @@ pub fn get_full_chunk_name( pub use rspack_util::identifier::request_to_id; -pub fn get_used_chunk_ids(chunk_by_ukey: &ChunkByUkey) -> FxHashSet { +pub fn get_used_chunk_ids(chunk_by_ukey: &ChunkSlotMap) -> FxHashSet { let mut used_ids = FxHashSet::default(); for chunk in chunk_by_ukey.values() { if let Some(id) = chunk.id() { @@ -679,7 +699,7 @@ pub fn get_used_chunk_ids(chunk_by_ukey: &ChunkByUkey) -> FxHashSet { used_ids } -pub fn assign_ascending_chunk_ids(chunks: &[ChunkUkey], chunk_by_ukey: &mut ChunkByUkey) { +pub fn assign_ascending_chunk_ids(chunks: &[ChunkUkey], chunk_by_ukey: &mut ChunkSlotMap) { let used_ids = get_used_chunk_ids(chunk_by_ukey); let mut next_id = 0; @@ -712,7 +732,7 @@ pub struct NaturalChunkCompareCache<'a> { } fn compare_chunks_by_modules<'a>( - chunk_graph: &ChunkGraph, + chunk_graph: &rspack_core::ChunkGraphTopology, module_ids: &'a ModuleIdsArtifact, a: &Chunk, b: &Chunk, @@ -842,7 +862,7 @@ fn compare_chunks_by_groups( } pub fn compare_chunks_natural<'a>( - chunk_graph: &ChunkGraph, + chunk_graph: &rspack_core::ChunkGraphTopology, chunk_group_by_ukey: &ChunkGroupByUkey, module_ids: &'a ModuleIdsArtifact, a: &Chunk, diff --git a/crates/rspack_ids/src/named_chunk_ids_plugin.rs b/crates/rspack_ids/src/named_chunk_ids_plugin.rs index c13d3c3a2b39..000e69e07b00 100644 --- a/crates/rspack_ids/src/named_chunk_ids_plugin.rs +++ b/crates/rspack_ids/src/named_chunk_ids_plugin.rs @@ -1,11 +1,10 @@ use rayon::iter::{IntoParallelIterator, ParallelBridge, ParallelIterator}; use rspack_core::{ - ChunkByUkey, ChunkGraph, ChunkGroupByUkey, ChunkNamedIdArtifact, ChunkUkey, CompilationChunkIds, - ExportsInfoArtifact, Logger, ModuleGraph, ModuleGraphCacheArtifact, Plugin, + Chunk, ChunkGroupByUkey, ChunkNamedIdArtifact, ChunkSet, ChunkSlotMap, ChunkUkey, + CompilationChunkIds, ExportsInfoArtifact, Logger, ModuleGraph, ModuleGraphCacheArtifact, Plugin, chunk_graph_chunk::{ChunkId, ChunkIdMap, ChunkIdSet}, incremental::{self, IncrementalPasses, Mutation, Mutations}, }; -use rspack_error::Diagnostic; use rspack_hook::{plugin, plugin_hook}; use rspack_util::{fx_hash::FxIndexSet, itoa}; use rustc_hash::FxHashSet; @@ -17,9 +16,9 @@ use crate::id_helpers::{ #[tracing::instrument(skip_all)] #[allow(clippy::too_many_arguments)] fn assign_named_chunk_ids( - chunks: FxHashSet, - chunk_by_ukey: &mut ChunkByUkey, - chunk_graph: &ChunkGraph, + chunks: ChunkSet, + chunk_by_ukey: &mut ChunkSlotMap, + chunk_graph: &rspack_core::ChunkGraphTopology, chunk_group_by_ukey: &ChunkGroupByUkey, module_ids_artifact: &rspack_core::ModuleIdsArtifact, context: &str, @@ -33,7 +32,9 @@ fn assign_named_chunk_ids( mutations: &mut Option, ) -> Vec { let chunks_len = chunks.len(); - let item_name_pair: Vec<_> = chunks + let mut chunk_keys = Vec::with_capacity(chunks_len); + chunk_keys.extend(chunks.into_keys()); + let item_name_pair: Vec<_> = chunk_keys .into_par_iter() .map(|item| { let chunk = chunk_by_ukey.expect_get(&item); @@ -183,6 +184,7 @@ fn assign_named_chunk_ids( b, &mut chunk_compare_cache, ) + .then_with(|| a.ukey().cmp(&b.ukey())) }); let mut i = 0; for item in items { @@ -219,6 +221,7 @@ fn assign_named_chunk_ids( b, &mut chunk_compare_cache, ) + .then_with(|| a.ukey().cmp(&b.ukey())) }); unnamed_items } @@ -237,13 +240,12 @@ impl NamedChunkIdsPlugin { } #[plugin_hook(CompilationChunkIds for NamedChunkIdsPlugin)] -async fn chunk_ids( - &self, - compilation: &rspack_core::Compilation, - chunk_by_ukey: &mut ChunkByUkey, - named_chunk_ids_artifact: &mut ChunkNamedIdArtifact, - _diagnostics: &mut Vec, -) -> rspack_error::Result<()> { +async fn chunk_ids(&self, compilation: &mut rspack_core::Compilation) -> rspack_error::Result<()> { + let logger = compilation.get_logger("rspack.incremental.chunkIds"); + let artifact = &mut compilation.build_chunk_graph_artifact; + let chunk_by_ukey = &mut artifact.chunk_graph.chunks; + let named_chunk_ids_artifact = &mut *compilation.named_chunk_ids_artifact; + if let Some(mutations) = compilation .incremental .mutations_read(IncrementalPasses::CHUNK_IDS) @@ -256,10 +258,7 @@ async fn chunk_ids( named_chunk_ids_artifact.remove(chunk); } Mutation::ModuleSetId { module } => { - let chunks = compilation - .build_chunk_graph_artifact - .chunk_graph - .get_module_chunks(*module); + let chunks = artifact.chunk_graph.topology.get_module_chunks(*module); affected_chunks.extend(chunks.iter().copied()); } _ => {} @@ -270,15 +269,17 @@ async fn chunk_ids( .retain(|chunk| chunk_by_ukey.contains(chunk) && !affected_chunks.contains(chunk)); } - let mut chunks: FxHashSet = chunk_by_ukey - .values_mut() - .map(|chunk| { + if !named_chunk_ids_artifact.chunk_ids.is_empty() { + for chunk in chunk_by_ukey.values_mut() { if let Some(id) = named_chunk_ids_artifact.chunk_ids.get(&chunk.ukey()) { chunk.set_id(id.clone()); } - chunk.ukey() - }) - .collect(); + } + } + let mut chunks = ChunkSet::with_capacity(chunk_by_ukey.slot_count()); + for key in chunk_by_ukey.keys() { + chunks.insert(chunk_by_ukey, *key); + } let chunks_len = chunks.len(); @@ -306,13 +307,13 @@ async fn chunk_ids( }); let named_chunks_len = chunks_len - chunks.len(); - let module_graph = compilation.get_module_graph(); + let module_graph = compilation.build_module_graph_artifact.get_module_graph(); let context = compilation.options.context.as_str(); let unnamed_chunks = assign_named_chunk_ids( chunks, chunk_by_ukey, - &compilation.build_chunk_graph_artifact.chunk_graph, - &compilation.build_chunk_graph_artifact.chunk_group_by_ukey, + &artifact.chunk_graph.topology, + &artifact.chunk_group_by_ukey, &compilation.module_ids_artifact, context, module_graph, @@ -352,7 +353,6 @@ async fn chunk_ids( .mutations_readable(IncrementalPasses::CHUNK_IDS) && let Some(mutations) = &mutations { - let logger = compilation.get_logger("rspack.incremental.chunkIds"); logger.log(format!( "{} chunks are updated by set_chunk_id, with {} chunks using name as id, and {} unnamed chunks", mutations.len(), diff --git a/crates/rspack_ids/src/natural_chunk_ids_plugin.rs b/crates/rspack_ids/src/natural_chunk_ids_plugin.rs index e15780c6cca6..c95c21207a36 100644 --- a/crates/rspack_ids/src/natural_chunk_ids_plugin.rs +++ b/crates/rspack_ids/src/natural_chunk_ids_plugin.rs @@ -1,9 +1,5 @@ use itertools::Itertools; -use rspack_core::{ - Chunk, ChunkByUkey, ChunkNamedIdArtifact, CompilationChunkIds, Plugin, - incremental::IncrementalPasses, -}; -use rspack_error::Diagnostic; +use rspack_core::{Chunk, CompilationChunkIds, Plugin, incremental::IncrementalPasses}; use rspack_hook::{plugin, plugin_hook}; use crate::id_helpers::{ @@ -15,22 +11,17 @@ use crate::id_helpers::{ pub struct NaturalChunkIdsPlugin; #[plugin_hook(CompilationChunkIds for NaturalChunkIdsPlugin)] -async fn chunk_ids( - &self, - compilation: &rspack_core::Compilation, - chunk_by_ukey: &mut ChunkByUkey, - _named_chunk_ids_artifact: &mut ChunkNamedIdArtifact, - diagnostics: &mut Vec, -) -> rspack_error::Result<()> { +async fn chunk_ids(&self, compilation: &mut rspack_core::Compilation) -> rspack_error::Result<()> { if let Some(diagnostic) = compilation.incremental.disable_passes( IncrementalPasses::CHUNK_IDS | IncrementalPasses::MODULES_HASHES, "NaturalChunkIdsPlugin (optimization.chunkIds = \"natural\")", "it requires calculating the id of all the chunks, which is a global effect", ) && let Some(diagnostic) = diagnostic { - diagnostics.push(diagnostic); + compilation.extend_diagnostics([diagnostic]); } + let chunk_by_ukey = &compilation.build_chunk_graph_artifact.chunk_graph.chunks; let module_ids = &compilation.module_ids_artifact; let chunk_graph = &compilation.build_chunk_graph_artifact.chunk_graph; let mut chunk_compare_cache = NaturalChunkCompareCache::default(); @@ -52,7 +43,10 @@ async fn chunk_ids( .collect::>(); if !chunks.is_empty() { - assign_ascending_chunk_ids(&chunks, chunk_by_ukey); + assign_ascending_chunk_ids( + &chunks, + &mut compilation.build_chunk_graph_artifact.chunk_graph.chunks, + ); } Ok(()) diff --git a/crates/rspack_ids/src/occurrence_chunk_ids_plugin.rs b/crates/rspack_ids/src/occurrence_chunk_ids_plugin.rs index 1fa4d5575370..663e667a3e27 100644 --- a/crates/rspack_ids/src/occurrence_chunk_ids_plugin.rs +++ b/crates/rspack_ids/src/occurrence_chunk_ids_plugin.rs @@ -1,9 +1,6 @@ use itertools::Itertools; -use rspack_core::{ - Chunk, ChunkByUkey, ChunkNamedIdArtifact, CompilationChunkIds, Plugin, - incremental::IncrementalPasses, -}; -use rspack_error::{Diagnostic, Result}; +use rspack_core::{Chunk, CompilationChunkIds, Plugin, incremental::IncrementalPasses}; +use rspack_error::Result; use rspack_hook::{plugin, plugin_hook}; use rustc_hash::FxHashMap as HashMap; @@ -29,22 +26,17 @@ impl OccurrenceChunkIdsPlugin { } #[plugin_hook(CompilationChunkIds for OccurrenceChunkIdsPlugin)] -async fn chunk_ids( - &self, - compilation: &rspack_core::Compilation, - chunk_by_ukey: &mut ChunkByUkey, - _named_chunk_ids_artifact: &mut ChunkNamedIdArtifact, - diagnostics: &mut Vec, -) -> Result<()> { +async fn chunk_ids(&self, compilation: &mut rspack_core::Compilation) -> Result<()> { if let Some(diagnostic) = compilation.incremental.disable_passes( IncrementalPasses::CHUNK_IDS | IncrementalPasses::MODULES_HASHES, "OccurrenceChunkIdsPlugin (optimization.chunkIds = \"size\")", "it requires calculating the id of all the chunks, which is a global effect", ) && let Some(diagnostic) = diagnostic { - diagnostics.push(diagnostic); + compilation.extend_diagnostics([diagnostic]); } + let chunk_by_ukey = &compilation.build_chunk_graph_artifact.chunk_graph.chunks; let chunk_graph = &compilation.build_chunk_graph_artifact.chunk_graph; let chunk_group_by_ukey = &compilation.build_chunk_graph_artifact.chunk_group_by_ukey; let mut occurs_in_initial_chunks_map = HashMap::default(); @@ -96,7 +88,10 @@ async fn chunk_ids( .map(|chunk| chunk.ukey()) .collect::>(); - assign_ascending_chunk_ids(&chunks, chunk_by_ukey); + assign_ascending_chunk_ids( + &chunks, + &mut compilation.build_chunk_graph_artifact.chunk_graph.chunks, + ); Ok(()) } diff --git a/crates/rspack_plugin_asset/src/lib.rs b/crates/rspack_plugin_asset/src/lib.rs index 27e1099353ce..b87edd775488 100644 --- a/crates/rspack_plugin_asset/src/lib.rs +++ b/crates/rspack_plugin_asset/src/lib.rs @@ -887,7 +887,8 @@ async fn render_manifest( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let module_graph = compilation.get_module_graph(); diff --git a/crates/rspack_plugin_banner/src/lib.rs b/crates/rspack_plugin_banner/src/lib.rs index 8ae8ec885ef4..1b39d41616fe 100644 --- a/crates/rspack_plugin_banner/src/lib.rs +++ b/crates/rspack_plugin_banner/src/lib.rs @@ -138,7 +138,8 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { // filter file for chunk in compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .values() { let can_be_initial = diff --git a/crates/rspack_plugin_css/src/plugin/impl_plugin_for_css_plugin.rs b/crates/rspack_plugin_css/src/plugin/impl_plugin_for_css_plugin.rs index ff2bb4009cc5..65abde34c7a8 100644 --- a/crates/rspack_plugin_css/src/plugin/impl_plugin_for_css_plugin.rs +++ b/crates/rspack_plugin_css/src/plugin/impl_plugin_for_css_plugin.rs @@ -128,7 +128,8 @@ impl CssPlugin { diagnostics.extend(conflicts.into_iter().map(|conflict| { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&conflict.chunk); let failed_module = mg @@ -150,7 +151,7 @@ impl CssPlugin { ), ); diagnostic.file = Some(output_path.to_owned().into()); - diagnostic.chunk = Some(chunk.ukey().as_u32()); + diagnostic.chunk = Some(chunk.ukey().as_u64()); diagnostic })); } @@ -211,7 +212,7 @@ impl CssPlugin { } }; - let chunk_ukey = chunk.as_u32().into(); + let chunk_ukey = chunk; // Module package hooks may add module-specific decorations such as pathinfo. // Deduplicate the undecorated source but emit the rendered source. let source_before_hooks = post_module_container.source.clone(); @@ -527,7 +528,8 @@ async fn content_hash( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let module_graph = compilation.get_module_graph(); let css_import_modules = compilation @@ -574,7 +576,8 @@ async fn render_manifest( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let _runtime_template = compilation .runtime_template diff --git a/crates/rspack_plugin_css/src/runtime/mod.rs b/crates/rspack_plugin_css/src/runtime/mod.rs index 8c1be7db9e7e..84d9814393ad 100644 --- a/crates/rspack_plugin_css/src/runtime/mod.rs +++ b/crates/rspack_plugin_css/src/runtime/mod.rs @@ -248,7 +248,8 @@ impl RuntimeModule for CssLoadingRuntimeModule { if let Some(chunk_ukey) = self.chunk() { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); let runtime_requirements = get_chunk_runtime_requirements(compilation, &chunk_ukey); @@ -277,7 +278,8 @@ impl RuntimeModule for CssLoadingRuntimeModule { for chunk_ukey in initial_chunks.iter() { let id = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey) .expect_id() .clone(); diff --git a/crates/rspack_plugin_css_chunking/src/lib.rs b/crates/rspack_plugin_css_chunking/src/lib.rs index cdbca87bb177..14e087d723c1 100644 --- a/crates/rspack_plugin_css_chunking/src/lib.rs +++ b/crates/rspack_plugin_css_chunking/src/lib.rs @@ -87,7 +87,7 @@ async fn optimize_chunks(&self, compilation: &mut Compilation) -> Result Result = Vec::with_capacity(ordered_modules.len()); @@ -369,17 +370,20 @@ async fn optimize_chunks(&self, compilation: &mut Compilation) -> Result Result Result<()> { let mut file_to_chunk_ukey: HashMap = HashMap::default(); for chunk in compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .values() { for file in chunk.files() { @@ -1356,7 +1357,8 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { let chunk = chunk_ukey.map(|ukey| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(ukey) }); if let Some(chunk) = chunk { diff --git a/crates/rspack_plugin_dll/src/lib_manifest_plugin.rs b/crates/rspack_plugin_dll/src/lib_manifest_plugin.rs index ee6b8d405039..fb90766561da 100644 --- a/crates/rspack_plugin_dll/src/lib_manifest_plugin.rs +++ b/crates/rspack_plugin_dll/src/lib_manifest_plugin.rs @@ -57,7 +57,12 @@ async fn emit(&self, compilation: &mut Compilation) -> Result<()> { let module_graph = compilation.get_module_graph(); - for (_, chunk) in compilation.build_chunk_graph_artifact.chunk_by_ukey.iter() { + for (_, chunk) in compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .iter() + { if !chunk.can_be_initial(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) { continue; } diff --git a/crates/rspack_plugin_ensure_chunk_conditions/src/lib.rs b/crates/rspack_plugin_ensure_chunk_conditions/src/lib.rs index 363e3ed68a02..608647810851 100644 --- a/crates/rspack_plugin_ensure_chunk_conditions/src/lib.rs +++ b/crates/rspack_plugin_ensure_chunk_conditions/src/lib.rs @@ -57,7 +57,8 @@ async fn optimize_chunks(&self, compilation: &mut Compilation) -> Result>(); diff --git a/crates/rspack_plugin_esm_library/src/dependency/dyn_import.rs b/crates/rspack_plugin_esm_library/src/dependency/dyn_import.rs index 365c09492d17..fb48e0bbc903 100644 --- a/crates/rspack_plugin_esm_library/src/dependency/dyn_import.rs +++ b/crates/rspack_plugin_esm_library/src/dependency/dyn_import.rs @@ -306,7 +306,8 @@ impl DynamicImportDependencyTemplate { let ref_chunk = code_generatable_context .compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&ref_chunk_ukey); let import_promise = if already_in_chunk { Cow::Borrowed("Promise.resolve()") diff --git a/crates/rspack_plugin_esm_library/src/link.rs b/crates/rspack_plugin_esm_library/src/link.rs index fed00512cc04..cbe85beb52ec 100644 --- a/crates/rspack_plugin_esm_library/src/link.rs +++ b/crates/rspack_plugin_esm_library/src/link.rs @@ -293,7 +293,8 @@ impl EsmLibraryPlugin { // allocated before bundled top-level declarations and participate in the same deconfliction. let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_link.chunk); let decl_modules = chunk_link.decl_modules.iter().copied().collect::>(); @@ -533,7 +534,8 @@ impl EsmLibraryPlugin { // initialize data for link chunks let mut link: FxHashMap = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .keys() .map(|ukey| { let modules = compilation @@ -2127,13 +2129,15 @@ var {} = {{}}; // chunk_link.required ahead. let mut exports = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .keys() .map(|chunk| (*chunk, Default::default())) .collect::>(); let mut imports = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .keys() .map(|chunk| (*chunk, Default::default())) .collect::>>>(); @@ -3179,7 +3183,7 @@ fn normal_render( #[cfg(test)] mod tests { use rspack_core::{ - ChunkInitFragments, ChunkUkey, ConcatenationNameAllocator, InitFragmentKey, ModuleIdentifier, + ChunkInitFragments, ConcatenationNameAllocator, InitFragmentKey, ModuleIdentifier, }; use rspack_intern::Atom; use rspack_util::fx_hash::{FxHashMap, FxHashSet}; @@ -3203,8 +3207,16 @@ mod tests { fn get_module_chunk_multiple_chunks_returns_error() { let m = ModuleIdentifier::from("test_module"); let mut chunks = FxHashSet::default(); - chunks.insert(ChunkUkey::new()); - chunks.insert(ChunkUkey::new()); + chunks.insert( + rspack_core::ChunkGraph::default() + .create_chunk(None, rspack_core::ChunkKind::Normal) + .expect("test Chunk allocation"), + ); + chunks.insert( + rspack_core::ChunkGraph::default() + .create_chunk(None, rspack_core::ChunkKind::Normal) + .expect("test Chunk allocation"), + ); let result = EsmLibraryPlugin::validate_single_chunk(m, &chunks); assert!(result.is_err()); let err_msg = result.unwrap_err().to_string(); @@ -3217,7 +3229,9 @@ mod tests { #[test] fn get_module_chunk_single_chunk_returns_ok() { let m = ModuleIdentifier::from("test_module"); - let expected_chunk = ChunkUkey::new(); + let expected_chunk = rspack_core::ChunkGraph::default() + .create_chunk(None, rspack_core::ChunkKind::Normal) + .expect("test Chunk allocation"); let mut chunks = FxHashSet::default(); chunks.insert(expected_chunk); let result = EsmLibraryPlugin::validate_single_chunk(m, &chunks); diff --git a/crates/rspack_plugin_esm_library/src/optimize_chunks.rs b/crates/rspack_plugin_esm_library/src/optimize_chunks.rs index b4b336eb2555..1d84595bb07c 100644 --- a/crates/rspack_plugin_esm_library/src/optimize_chunks.rs +++ b/crates/rspack_plugin_esm_library/src/optimize_chunks.rs @@ -24,7 +24,9 @@ use crate::EsmLibraryPlugin; /// /// This function extracts such shared modules into separate chunks to break /// the cycle. It returns `true` if modules were actually extracted. -pub(crate) fn extract_tla_shared_modules(compilation: &mut Compilation) -> bool { +pub(crate) fn extract_tla_shared_modules( + compilation: &mut Compilation, +) -> rspack_error::Result { let module_graph = compilation.get_module_graph(); let chunk_graph = &compilation.build_chunk_graph_artifact.chunk_graph; let chunk_group_by_ukey = &compilation.build_chunk_graph_artifact.chunk_group_by_ukey; @@ -61,7 +63,8 @@ pub(crate) fn extract_tla_shared_modules(compilation: &mut Compilation) -> bool for &chunk_ukey in chunk_graph.get_module_chunks(*target) { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); if !chunk.is_only_initial(chunk_group_by_ukey) { async_chunks_set.insert(chunk_ukey); @@ -72,7 +75,7 @@ pub(crate) fn extract_tla_shared_modules(compilation: &mut Compilation) -> bool } if async_chunks_set.is_empty() { - return false; + return Ok(false); } // Phase 2 + 3: For each at-risk async chunk, compute its ancestor chunks and @@ -88,7 +91,8 @@ pub(crate) fn extract_tla_shared_modules(compilation: &mut Compilation) -> bool // Collect ancestor chunks via chunk group parent traversal let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&async_chunk_ukey); let mut ancestor_groups = FxHashSet::default(); for group_ukey in chunk.groups() { @@ -167,7 +171,7 @@ pub(crate) fn extract_tla_shared_modules(compilation: &mut Compilation) -> bool } if modules_to_extract.is_empty() { - return false; + return Ok(false); } // Phase 4: Group modules by their exact source-chunk set (modules that live @@ -181,8 +185,10 @@ pub(crate) fn extract_tla_shared_modules(compilation: &mut Compilation) -> bool } for (source_chunks, modules) in chunk_group_map { - let new_chunk_ukey = - Compilation::add_chunk(&mut compilation.build_chunk_graph_artifact.chunk_by_ukey); + let new_chunk_ukey = compilation + .build_chunk_graph_artifact + .chunk_graph + .create_chunk(None, rspack_core::ChunkKind::Normal)?; if let Some(mut mutations) = compilation.incremental.mutations_write() { mutations.add(Mutation::ChunkAdd { chunk: new_chunk_ukey, @@ -191,14 +197,11 @@ pub(crate) fn extract_tla_shared_modules(compilation: &mut Compilation) -> bool { let new_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&new_chunk_ukey); *new_chunk.chunk_reason_mut() = Some("extracted to break TLA circular dependency".into()); } - compilation - .build_chunk_graph_artifact - .chunk_graph - .add_chunk(new_chunk_ukey); // For each module: collect which source chunks had it registered as an // entry module, then disconnect from all source chunks and connect to the @@ -238,17 +241,14 @@ pub(crate) fn extract_tla_shared_modules(compilation: &mut Compilation) -> bool // Establish chunk group relationships: new_chunk joins each source chunk's // groups (making it a sibling — it will be loaded alongside the source). for source_chunk_ukey in &source_chunks { - let [Some(source_chunk), Some(new_chunk)] = compilation + compilation .build_chunk_graph_artifact - .chunk_by_ukey - .get_many_mut([source_chunk_ukey, &new_chunk_ukey]) - else { - unreachable!("both chunks should exist") - }; - source_chunk.split( - new_chunk, - &mut compilation.build_chunk_graph_artifact.chunk_group_by_ukey, - ); + .chunk_graph + .split_chunk( + source_chunk_ukey, + &new_chunk_ukey, + &mut compilation.build_chunk_graph_artifact.chunk_group_by_ukey, + ); if let Some(mut mutations) = compilation.incremental.mutations_write() { mutations.add(Mutation::ChunkSplit { from: *source_chunk_ukey, @@ -258,7 +258,7 @@ pub(crate) fn extract_tla_shared_modules(compilation: &mut Compilation) -> bool } } - true + Ok(true) } /// Ensure that all entry chunks only export the exports used by other chunks, @@ -268,7 +268,7 @@ pub(crate) fn extract_tla_shared_modules(compilation: &mut Compilation) -> bool /// entry chunk: a, b /// async chunk: c /// c depends on a, so entry chunk needs to re-export symbols from a -pub(crate) fn ensure_entry_exports(compilation: &mut Compilation) { +pub(crate) fn ensure_entry_exports(compilation: &mut Compilation) -> rspack_error::Result<()> { let module_graph = compilation.get_module_graph(); let mut entrypoint_chunks = FxHashMap::::default(); let mut entry_module_belongs: IdentifierMap> = IdentifierMap::default(); @@ -302,7 +302,8 @@ pub(crate) fn ensure_entry_exports(compilation: &mut Compilation) { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .iter() .par_bridge() .filter(|(ukey, _)| !entrypoint_chunks.contains_key(ukey)) @@ -348,17 +349,15 @@ pub(crate) fn ensure_entry_exports(compilation: &mut Compilation) { .copied() .collect::>(); - let new_chunk_ukey = - Compilation::add_chunk(&mut compilation.build_chunk_graph_artifact.chunk_by_ukey); + let new_chunk_ukey = compilation + .build_chunk_graph_artifact + .chunk_graph + .create_chunk(None, rspack_core::ChunkKind::Normal)?; if let Some(mut mutation) = compilation.incremental.mutations_write() { mutation.add(Mutation::ChunkAdd { chunk: new_chunk_ukey, }); } - compilation - .build_chunk_graph_artifact - .chunk_graph - .add_chunk(new_chunk_ukey); // move entrypoint runtime as well let entrypoint_ukey = entrypoint_chunks[&entry_chunk_ukey]; @@ -398,19 +397,16 @@ pub(crate) fn ensure_entry_exports(compilation: &mut Compilation) { .connect_chunk_and_entry_module(new_chunk_ukey, m, entrypoint); } - let [Some(entry_chunk), Some(new_chunk)] = compilation + compilation .build_chunk_graph_artifact - .chunk_by_ukey - .get_many_mut([&entry_chunk_ukey, &new_chunk_ukey]) - else { - unreachable!() - }; - - entry_chunk.split( - new_chunk, - &mut compilation.build_chunk_graph_artifact.chunk_group_by_ukey, - ); + .chunk_graph + .split_chunk( + &entry_chunk_ukey, + &new_chunk_ukey, + &mut compilation.build_chunk_graph_artifact.chunk_group_by_ukey, + ); } + Ok(()) } /// For each entrypoint, if the runtime chunk is the same as the entry chunk @@ -420,7 +416,7 @@ pub(crate) fn ensure_entry_exports(compilation: &mut Compilation) { /// /// This must run AFTER SplitChunksPlugin and RemoveDuplicateModulesPlugin /// to inspect the final chunk graph topology. -pub(crate) fn optimize_runtime_chunks(compilation: &mut Compilation) { +pub(crate) fn optimize_runtime_chunks(compilation: &mut Compilation) -> rspack_error::Result<()> { // Phase 1: Collect entrypoints that need runtime splitting let entrypoints_to_split: Vec = compilation .entrypoints() @@ -443,7 +439,8 @@ pub(crate) fn optimize_runtime_chunks(compilation: &mut Compilation) { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&runtime_chunk_ukey); if chunk.has_async_chunks(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) { @@ -472,8 +469,10 @@ pub(crate) fn optimize_runtime_chunks(compilation: &mut Compilation) { }; // Create a new chunk - let new_chunk_ukey = - Compilation::add_chunk(&mut compilation.build_chunk_graph_artifact.chunk_by_ukey); + let new_chunk_ukey = compilation + .build_chunk_graph_artifact + .chunk_graph + .create_chunk(None, rspack_core::ChunkKind::Normal)?; // Record mutation for incremental compilation if let Some(mut mutation) = compilation.incremental.mutations_write() { @@ -487,10 +486,6 @@ pub(crate) fn optimize_runtime_chunks(compilation: &mut Compilation) { } // Register the chunk in the chunk graph - compilation - .build_chunk_graph_artifact - .chunk_graph - .add_chunk(new_chunk_ukey); // Set the entrypoint's runtime chunk to the new chunk let entrypoint = compilation @@ -501,26 +496,26 @@ pub(crate) fn optimize_runtime_chunks(compilation: &mut Compilation) { entrypoint.unshift_chunk(new_chunk_ukey); // Configure the new chunk - let [Some(entry_chunk), Some(new_chunk)] = compilation - .build_chunk_graph_artifact - .chunk_by_ukey - .get_many_mut([&entry_chunk_ukey, &new_chunk_ukey]) - else { - unreachable!("entry_chunk and new_chunk should both exist") - }; - - new_chunk.set_runtime(entry_chunk.runtime().clone()); + let chunk_graph = &mut compilation.build_chunk_graph_artifact.chunk_graph; + let runtime = chunk_graph + .chunks + .expect_get(&entry_chunk_ukey) + .runtime() + .clone(); + let new_chunk = chunk_graph.chunks.expect_get_mut(&new_chunk_ukey); + new_chunk.set_runtime(runtime); new_chunk.add_id_name_hints("runtime".to_string()); new_chunk.set_prevent_integration(true); new_chunk.add_group(entrypoint_ukey); } + Ok(()) } /// Mark module-less entrypoint chunks as facades after modern-module chunk /// optimizations have finished. pub(crate) fn mark_facade_chunks(compilation: &mut Compilation) { let artifact = &mut compilation.build_chunk_graph_artifact; - for chunk in artifact.chunk_by_ukey.values_mut() { + for chunk in artifact.chunk_graph.chunks.values_mut() { if chunk.kind() == ChunkKind::Facade { chunk.set_kind(ChunkKind::Normal); } @@ -545,7 +540,8 @@ pub(crate) fn mark_facade_chunks(compilation: &mut Compilation) { == 0 { artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&chunk_ukey) .set_kind(ChunkKind::Facade); } @@ -885,10 +881,20 @@ pub(crate) fn assign_dyn_import_chunk_short_names(compilation: &mut Compilation) // Collect candidates: (chunk_ukey, root_module_identifier) for unnamed non-initial chunks // with exactly one root module - let mut candidates: Vec<(ChunkUkey, ModuleIdentifier)> = - Vec::with_capacity(compilation.build_chunk_graph_artifact.chunk_by_ukey.len()); + let mut candidates: Vec<(ChunkUkey, ModuleIdentifier)> = Vec::with_capacity( + compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .len(), + ); - for (chunk_ukey, chunk) in compilation.build_chunk_graph_artifact.chunk_by_ukey.iter() { + for (chunk_ukey, chunk) in compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .iter() + { // Skip chunks that already have a name if chunk.name().is_some() { continue; @@ -979,7 +985,8 @@ pub(crate) fn assign_dyn_import_chunk_short_names(compilation: &mut Compilation) for (chunk_ukey, name) in assignments { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&chunk_ukey); chunk.set_name(Some(name.clone())); compilation diff --git a/crates/rspack_plugin_esm_library/src/plugin.rs b/crates/rspack_plugin_esm_library/src/plugin.rs index 1f0c88464f20..0aaeb72bcbda 100644 --- a/crates/rspack_plugin_esm_library/src/plugin.rs +++ b/crates/rspack_plugin_esm_library/src/plugin.rs @@ -368,10 +368,16 @@ async fn after_code_generation( ) -> Result<()> { let mut chunk_ids_to_ukey = FxHashMap::default(); - for chunk_ukey in compilation.build_chunk_graph_artifact.chunk_by_ukey.keys() { + for chunk_ukey in compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .keys() + { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if let Some(id) = chunk.id() { @@ -536,7 +542,8 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { let Some(chunk) = chunk_ids_to_ukey.get(chunk_id).map(|chunk_ukey| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_ukey) .expect("should have chunk for chunk ukey") }) else { @@ -607,7 +614,7 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { async fn optimize_chunks(&self, compilation: &mut Compilation) -> Result> { // check if we have to generate proxy chunks if let Some(preserve_modules_root) = &self.preserve_modules { - let errors = preserve_modules(preserve_modules_root, compilation).await; + let errors = preserve_modules(preserve_modules_root, compilation).await?; if !errors.is_empty() { compilation.extend_diagnostics(errors); } @@ -615,7 +622,7 @@ async fn optimize_chunks(&self, compilation: &mut Compilation) -> Result Result Result Result> { - optimize_runtime_chunks(compilation); + optimize_runtime_chunks(compilation)?; mark_facade_chunks(compilation); Ok(None) } diff --git a/crates/rspack_plugin_esm_library/src/preserve_modules.rs b/crates/rspack_plugin_esm_library/src/preserve_modules.rs index 25ace9ffe9d2..58d9e7ed78de 100644 --- a/crates/rspack_plugin_esm_library/src/preserve_modules.rs +++ b/crates/rspack_plugin_esm_library/src/preserve_modules.rs @@ -114,7 +114,7 @@ fn module_resource_path(module: &dyn Module) -> Option { pub async fn preserve_modules( root: &Path, compilation: &mut Compilation, -) -> Vec { +) -> rspack_error::Result> { let mut errors = vec![]; let modules = compilation .get_module_graph() @@ -225,7 +225,8 @@ pub async fn preserve_modules( let js_filename_template: Option = if has_js { let extension = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(&chunk) .and_then(|c| c.filename_template().cloned()) .unwrap_or_else(|| compilation.options.output.filename.clone()); @@ -270,7 +271,8 @@ pub async fn preserve_modules( // This is the last module in the chunk — rename in-place. let old_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&chunk); if let Some(old_name) = old_chunk.name().map(|s| s.to_string()) && old_name != base_name @@ -291,28 +293,28 @@ pub async fn preserve_modules( continue; } - let new_chunk_ukey = - Compilation::add_chunk(&mut compilation.build_chunk_graph_artifact.chunk_by_ukey); - compilation + let new_chunk_ukey = compilation .build_chunk_graph_artifact .chunk_graph - .add_chunk(new_chunk_ukey); - let [Some(new_chunk), Some(old_chunk)] = compilation + .create_chunk(None, rspack_core::ChunkKind::Normal)?; + let new_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey - .get_many_mut([&new_chunk_ukey, &chunk]) - else { - unreachable!("new_chunk and old_chunk should be inserted already") - }; + .chunk_graph + .chunks + .expect_get_mut(&new_chunk_ukey); new_chunk.set_name(Some(base_name.clone())); if let Some(template) = js_filename_template { new_chunk.set_filename_template(Some(template)); } - old_chunk.split( - new_chunk, - &mut compilation.build_chunk_graph_artifact.chunk_group_by_ukey, - ); + compilation + .build_chunk_graph_artifact + .chunk_graph + .split_chunk( + &chunk, + &new_chunk_ukey, + &mut compilation.build_chunk_graph_artifact.chunk_group_by_ukey, + ); compilation .build_chunk_graph_artifact .named_chunks @@ -350,7 +352,8 @@ pub async fn preserve_modules( // the new chunk that already owns that name. let old_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&chunk); if let Some(old_name) = old_chunk.name().map(|s| s.to_string()) { old_chunk.set_name(None); @@ -364,5 +367,5 @@ pub async fn preserve_modules( } } - errors + Ok(errors) } diff --git a/crates/rspack_plugin_esm_library/src/render.rs b/crates/rspack_plugin_esm_library/src/render.rs index e9d9eee44029..002ea3d4f0d6 100644 --- a/crates/rspack_plugin_esm_library/src/render.rs +++ b/crates/rspack_plugin_esm_library/src/render.rs @@ -77,7 +77,8 @@ fn replace_preserved_asset_import_binding( fn get_chunk(compilation: &Compilation, chunk_ukey: ChunkUkey) -> &Chunk { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey) } @@ -87,7 +88,8 @@ impl EsmLibraryPlugin { fn get_entrypoint(chunk_ukey: ChunkUkey, compilation: &Compilation) -> Option<&ChunkGroup> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); let group = chunk.groups().iter().next()?; let group = compilation @@ -547,7 +549,8 @@ var {} = {{}}; } let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk); if imported.is_empty() { @@ -691,7 +694,8 @@ var {} = {{}}; crate::chunk_link::ReExportFrom::Chunk(chunk_ukey) => { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); Cow::Owned(format!("__RSPACK_ESM_CHUNK_{}", chunk.expect_id().as_str())) } diff --git a/crates/rspack_plugin_esm_library/src/render/runtime_mode/mod.rs b/crates/rspack_plugin_esm_library/src/render/runtime_mode/mod.rs index e18fb65a7642..3ecc7075efb0 100644 --- a/crates/rspack_plugin_esm_library/src/render/runtime_mode/mod.rs +++ b/crates/rspack_plugin_esm_library/src/render/runtime_mode/mod.rs @@ -82,7 +82,8 @@ pub(super) trait RuntimeModeRenderer: Sync { fn get_chunk(compilation: &Compilation, chunk_ukey: ChunkUkey) -> &Chunk { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey) } diff --git a/crates/rspack_plugin_esm_library/src/runtime.rs b/crates/rspack_plugin_esm_library/src/runtime.rs index abc1bcdea141..0caf07549ba7 100644 --- a/crates/rspack_plugin_esm_library/src/runtime.rs +++ b/crates/rspack_plugin_esm_library/src/runtime.rs @@ -129,7 +129,8 @@ impl RuntimeModule for EsmChunkLoadingRuntimeModule { let chunk_ukey = self.chunk().expect("should have chunk"); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); let runtime = chunk.runtime().clone(); let initial_chunks = @@ -143,7 +144,8 @@ impl RuntimeModule for EsmChunkLoadingRuntimeModule { .map(|chunk_ukey| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey) }) .filter(|chunk| !chunk.runtime().is_disjoint(&runtime)) diff --git a/crates/rspack_plugin_esm_library/src/split_chunks.rs b/crates/rspack_plugin_esm_library/src/split_chunks.rs index 1de12b56cfa5..013171b3b620 100644 --- a/crates/rspack_plugin_esm_library/src/split_chunks.rs +++ b/crates/rspack_plugin_esm_library/src/split_chunks.rs @@ -313,11 +313,9 @@ pub(crate) async fn split(groups: &[CacheGroup], compilation: &mut Compilation) }; let chunk_ukey = if let Some(chunk_name) = chunk_name { - let (ukey, created) = Compilation::add_named_chunk( - chunk_name.clone(), - &mut compilation.build_chunk_graph_artifact.chunk_by_ukey, - &mut compilation.build_chunk_graph_artifact.named_chunks, - ); + let (ukey, created) = compilation + .build_chunk_graph_artifact + .add_named_chunk(chunk_name.clone())?; if !created { compilation.push_diagnostic(rspack_error::Diagnostic::warn( @@ -329,17 +327,16 @@ pub(crate) async fn split(groups: &[CacheGroup], compilation: &mut Compilation) ukey } else { - Compilation::add_chunk(&mut compilation.build_chunk_graph_artifact.chunk_by_ukey) + compilation + .build_chunk_graph_artifact + .chunk_graph + .create_chunk(None, rspack_core::ChunkKind::Normal)? }; - compilation - .build_chunk_graph_artifact - .chunk_graph - .add_chunk(chunk_ukey); - let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&chunk_ukey); if let Some(filename_template) = match_group.filename_template { chunk.set_filename_template(Some(filename_template)); @@ -384,29 +381,27 @@ pub(crate) async fn split(groups: &[CacheGroup], compilation: &mut Compilation) let entrypoints = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&orig_chunk) .groups(); for entrypoint in entrypoints { compilation .build_chunk_graph_artifact .chunk_graph + .topology .connect_chunk_and_entry_module(chunk_ukey, *m, *entrypoint); } } - let [Some(chunk), Some(orig_chunk)] = compilation + compilation .build_chunk_graph_artifact - .chunk_by_ukey - .get_many_mut([&chunk_ukey, &orig_chunk]) - else { - unreachable!() - }; - - orig_chunk.split( - chunk, - &mut compilation.build_chunk_graph_artifact.chunk_group_by_ukey, - ); + .chunk_graph + .split_chunk( + &orig_chunk, + &chunk_ukey, + &mut compilation.build_chunk_graph_artifact.chunk_group_by_ukey, + ); } splitted_modules.extend(match_group.modules); diff --git a/crates/rspack_plugin_extract_css/src/plugin.rs b/crates/rspack_plugin_extract_css/src/plugin.rs index 19d70a7121ba..b96b6955b4ab 100644 --- a/crates/rspack_plugin_extract_css/src/plugin.rs +++ b/crates/rspack_plugin_extract_css/src/plugin.rs @@ -350,7 +350,8 @@ impl PluginCssExtract { diagnostics.extend(conflicts.into_iter().map(|conflict| { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&conflict.chunk); let fallback_module = module_graph .module_by_identifier(&conflict.fallback_module) @@ -398,7 +399,7 @@ despite it was not able to fulfill desired ordering with these modules: ), ); diagnostic.file = Some(filename.to_owned().into()); - diagnostic.chunk = Some(chunk.ukey().as_u32()); + diagnostic.chunk = Some(chunk.ukey().as_u64()); diagnostic })); } @@ -632,7 +633,8 @@ async fn content_hash( } let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let (used_modules, diagnostics) = @@ -668,7 +670,8 @@ async fn render_manifest( let module_graph = compilation.get_module_graph(); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if matches!(chunk.kind(), ChunkKind::HotUpdate) { diff --git a/crates/rspack_plugin_extract_css/src/runtime.rs b/crates/rspack_plugin_extract_css/src/runtime.rs index e373c183500c..74f83e160960 100644 --- a/crates/rspack_plugin_extract_css/src/runtime.rs +++ b/crates/rspack_plugin_extract_css/src/runtime.rs @@ -130,7 +130,8 @@ impl CssLoadingRuntimeModule { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&self.chunk().expect("should attached chunk")); for chunk in @@ -259,7 +260,8 @@ impl RuntimeModule for CssLoadingRuntimeModule { let chunk_ukey = self.chunk().expect("should attached chunk"); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); let runtime_requirements = get_chunk_runtime_requirements(compilation, &chunk_ukey); @@ -358,7 +360,7 @@ impl RuntimeModule for CssLoadingRuntimeModule { chunks .iter() .filter_map(|id| { - let css_chunk = compilation.build_chunk_graph_artifact.chunk_by_ukey.expect_get(id); + let css_chunk = compilation.build_chunk_graph_artifact.chunk_graph.chunks.expect_get(id); css_chunk.id().map(|id| { format!( @@ -386,7 +388,8 @@ impl RuntimeModule for CssLoadingRuntimeModule { { if let Some(id) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey) .id() { diff --git a/crates/rspack_plugin_hmr/src/lib.rs b/crates/rspack_plugin_hmr/src/lib.rs index 3dbec856fb0e..a509c864ff26 100644 --- a/crates/rspack_plugin_hmr/src/lib.rs +++ b/crates/rspack_plugin_hmr/src/lib.rs @@ -9,7 +9,7 @@ use atomic_refcell::AtomicRefCell; use hot_module_replacement::HotModuleReplacementRuntimeModule; use rspack_collections::IdentifierSet; use rspack_core::{ - AssetInfo, Chunk, ChunkGraph, ChunkKind, ChunkUkey, Compilation, + AssetInfo, ChunkGraph, ChunkKind, ChunkUkey, Compilation, CompilationAdditionalTreeRuntimeRequirements, CompilationAsset, CompilationContentHash, CompilationParams, CompilationProcessAssets, CompilationRecords, CompilerCompilation, DependencyType, LoaderContext, ModuleId, ModuleIdentifier, ModuleType, NormalModuleFactoryParser, @@ -113,7 +113,8 @@ async fn content_hash( LazyLock::new(|| SourceType::Custom("css/mini-extract".into())); let Some(chunk) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_ukey) else { return Ok(()); @@ -165,7 +166,8 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { current_css_hashes.extend(collected); let live_chunk_ids: ChunkIdSet = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .values() .filter(|chunk| chunk.kind() != ChunkKind::HotUpdate) .filter_map(|chunk| chunk.id().cloned()) @@ -228,7 +230,8 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { .collect(); let current_chunk_ukeys: ChunkIdMap = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .iter() .map(|(ukey, chunk)| (chunk.expect_id().clone(), *ukey)) .collect(); @@ -261,7 +264,8 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { let current_chunk = current_chunk_ukey.and_then(|ukey| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(&ukey) }); @@ -385,10 +389,7 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { let runtimes = compilation .build_chunk_graph_artifact .chunk_graph - .get_module_runtimes( - *module_identifier, - &compilation.build_chunk_graph_artifact.chunk_by_ukey, - ); + .get_module_runtimes(*module_identifier); if old_runtime == &new_runtime && runtimes.contains(&new_runtime) { let new_hash = compilation .code_generation_results @@ -406,42 +407,26 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { } if !new_modules.is_empty() || !new_runtime_modules.is_empty() { - let mut hot_update_chunk = Chunk::new(None, ChunkKind::HotUpdate); - hot_update_chunk.set_id(chunk_id.clone()); - hot_update_chunk.set_runtime(if let Some(current_chunk) = current_chunk { - current_chunk.runtime().clone() - } else { - new_runtime.clone() - }); - let ukey = hot_update_chunk.ukey(); - - if let Some(current_chunk) = current_chunk { - current_chunk - .groups() - .iter() - .for_each(|group| hot_update_chunk.add_group(*group)) - } - - // In webpack, there is no need to add HotUpdateChunk to compilation.chunks, - // because HotUpdateChunk is no longer used after generating the manifest. - // - // However, in Rspack, we need to add HotUpdateChunk to compilation.build_chunk_graph_artifact.chunk_by_ukey - // because during the manifest generation, HotUpdateChunk is passed to various plugins via the ukey. - // The plugins then use the ukey to query compilation.build_chunk_graph_artifact.chunk_by_ukey to get the HotUpdateChunk instance. - // Therefore, in Rspack, after the manifest is generated, we need to manually remove the HotUpdateChunk from compilation.chunks. - compilation + let runtime = + current_chunk.map_or_else(|| new_runtime.clone(), |chunk| chunk.runtime().clone()); + let groups = current_chunk + .map(|chunk| chunk.groups().iter().copied().collect::>()) + .unwrap_or_default(); + let ukey = compilation .build_chunk_graph_artifact - .chunk_by_ukey - .add(hot_update_chunk); - - // In webpack, compilation.chunkGraph uses a WeakMap to maintain the relationship between Chunks and Modules. - // This means the lifecycle of these data is tied to the Chunk, and they are garbage-collected when the Chunk is. - // - // In Rspack, we need to manually clean up the data in compilation.build_chunk_graph_artifact.chunk_graph after HotUpdateChunk is used. - compilation + .chunk_graph + .create_chunk(None, ChunkKind::HotUpdate)?; + let hot_update_chunk = compilation .build_chunk_graph_artifact .chunk_graph - .add_chunk(ukey); + .chunks + .expect_get_mut(&ukey); + hot_update_chunk.set_id(chunk_id.clone()); + hot_update_chunk.set_runtime(runtime); + for group in groups { + hot_update_chunk.add_group(group); + } + for module_identifier in &new_modules { compilation .build_chunk_graph_artifact @@ -465,29 +450,10 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { .call(compilation, &ukey, &mut manifest, &mut diagnostics) .await?; - // Manually clean up ChunkGraph and chunks - for module_identifier in new_modules { - compilation - .build_chunk_graph_artifact - .chunk_graph - .disconnect_chunk_and_module(&ukey, module_identifier); - } - for runtime_module in new_runtime_modules { - compilation - .build_chunk_graph_artifact - .chunk_graph - .disconnect_chunk_and_runtime_module(&ukey, &runtime_module); - } - compilation - .build_chunk_graph_artifact - .chunk_graph - .remove_chunk(&ukey); - #[allow(clippy::unwrap_used)] let hot_update_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey - .remove(&ukey) - .unwrap(); + .remove_chunk(&ukey) + .expect("hot update Chunk is live"); compilation.extend_diagnostics(diagnostics); @@ -538,7 +504,8 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { for (chunk_ukey, files) in updated_chunks { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&chunk_ukey); for file in files { chunk.add_file(file); diff --git a/crates/rspack_plugin_html/src/asset.rs b/crates/rspack_plugin_html/src/asset.rs index 3894d7c1bd8e..43dd01023957 100644 --- a/crates/rspack_plugin_html/src/asset.rs +++ b/crates/rspack_plugin_html/src/asset.rs @@ -82,7 +82,7 @@ impl HtmlPluginAssets { let included_assets = sorted_entry_names .iter() .map(|entry_name| compilation.entrypoint_by_name(entry_name)) - .flat_map(|entry| entry.get_files(&compilation.build_chunk_graph_artifact.chunk_by_ukey)) + .flat_map(|entry| entry.get_files(&compilation.build_chunk_graph_artifact.chunk_graph.chunks)) .filter_map(|asset_name| { let asset = compilation .assets() diff --git a/crates/rspack_plugin_javascript/src/dependency/worker/mod.rs b/crates/rspack_plugin_javascript/src/dependency/worker/mod.rs index 84bb11104d82..6fba5028e470 100644 --- a/crates/rspack_plugin_javascript/src/dependency/worker/mod.rs +++ b/crates/rspack_plugin_javascript/src/dependency/worker/mod.rs @@ -194,7 +194,8 @@ impl DependencyTemplate for WorkerDependencyTemplate { .and_then(|ukey| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(&ukey) }) .and_then(|chunk| chunk.id()) diff --git a/crates/rspack_plugin_javascript/src/plugin/impl_plugin_for_js_plugin.rs b/crates/rspack_plugin_javascript/src/plugin/impl_plugin_for_js_plugin.rs index c583659fe5cf..65c89aaaa8f9 100644 --- a/crates/rspack_plugin_javascript/src/plugin/impl_plugin_for_js_plugin.rs +++ b/crates/rspack_plugin_javascript/src/plugin/impl_plugin_for_js_plugin.rs @@ -474,7 +474,8 @@ async fn chunk_hash( self.get_chunk_hash(chunk_ukey, compilation, hasher).await?; if compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey) .has_runtime(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) { @@ -494,7 +495,8 @@ async fn content_hash( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let hasher = hashes .entry(SourceType::JavaScript) @@ -555,7 +557,8 @@ async fn render_manifest( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let filename_template = get_js_chunk_filename_template( chunk, @@ -721,7 +724,8 @@ fn module_has_js(module_identifier: &ModuleIdentifier, module_graph: &ModuleGrap fn is_js_entry_chunk(chunk_ukey: &ChunkUkey, compilation: &Compilation) -> bool { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let module_graph = compilation.get_module_graph(); // ESM optimizations can move both the entry module and its chunk graph entry @@ -732,7 +736,7 @@ fn is_js_entry_chunk(chunk_ukey: &ChunkUkey, compilation: &Compilation) -> bool .chunk_group_by_ukey .expect_get(group_ukey); group.is_initial() - && group.get_entrypoint_chunk() == *chunk_ukey + && group.is_entrypoint_chunk(chunk_ukey) && group .name() .and_then(|name| compilation.entries.get(name)) @@ -774,7 +778,8 @@ fn chunk_has_required_runtime(chunk_ukey: &ChunkUkey, compilation: &Compilation) let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); for group_ukey in chunk.groups() { let chunk_group = compilation diff --git a/crates/rspack_plugin_javascript/src/plugin/mod.rs b/crates/rspack_plugin_javascript/src/plugin/mod.rs index 4f4e1d6c8605..460213e4167a 100644 --- a/crates/rspack_plugin_javascript/src/plugin/mod.rs +++ b/crates/rspack_plugin_javascript/src/plugin/mod.rs @@ -275,7 +275,8 @@ var module = ({module_cache}[moduleId] = {{"#, .unwrap_or_default(); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let module_factories = runtime_requirements.contains(RuntimeGlobals::MODULE_FACTORIES); let require_function = runtime_requirements.contains(RuntimeGlobals::REQUIRE); @@ -432,7 +433,8 @@ var {} = {{}}; .map(|chunk_ukey| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey) .expect_id() .to_string() @@ -462,10 +464,7 @@ var {} = {{}}; }) && compilation .build_chunk_graph_artifact .chunk_graph - .get_module_runtimes_iter( - *origin_module, - &compilation.build_chunk_graph_artifact.chunk_by_ukey, - ) + .get_module_runtimes_iter(*origin_module) .any(|runtime| runtime.intersection(chunk.runtime()).count() > 0) }) } { @@ -728,7 +727,8 @@ var {} = {{}}; .expect("should have js plugin drive"); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let supports_arrow_function = compilation .options @@ -1222,7 +1222,8 @@ var {} = {{}}; }; let runtime = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey) .runtime(); @@ -1240,7 +1241,8 @@ var {} = {{}}; let module_ident = m.identifier(); let runtime = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey) .runtime(); diff --git a/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs b/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs index 9baebb2dbb60..218eb70c1ecd 100644 --- a/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs +++ b/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs @@ -110,14 +110,16 @@ impl ConcatenationProblem { match self { Self::MissingChunks { root_chunks, .. } => { - let chunk_by_ukey = &compilation.build_chunk_graph_artifact.chunk_by_ukey; let chunk_graph = &compilation.build_chunk_graph_artifact.chunk_graph; let module_chunks = chunk_graph.get_module_chunks(module); let mut missing_chunks = root_chunks .iter() .filter(|chunk| !module_chunks.contains(*chunk)) .map(|chunk| { - chunk_by_ukey + compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks .expect_get(chunk) .name() .unwrap_or("unnamed chunk(s)") @@ -128,7 +130,10 @@ impl ConcatenationProblem { let mut chunks = module_chunks .iter() .map(|chunk| { - chunk_by_ukey + compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks .expect_get(chunk) .name() .unwrap_or("unnamed chunk(s)") @@ -543,7 +548,6 @@ impl ModuleConcatenationPlugin { let runtime = context.runtime; let module_cache = context.module_cache; let chunk_graph = &compilation.build_chunk_graph_artifact.chunk_graph; - let chunk_by_ukey = &compilation.build_chunk_graph_artifact.chunk_by_ukey; let module_graph = compilation.get_module_graph(); let module_graph_artifacts = context.module_graph_artifacts(); let mut modules = Vec::with_capacity(cached.incomings.from_modules.len()); @@ -567,9 +571,8 @@ impl ModuleConcatenationPlugin { { !runtime.is_disjoint(origin_runtime) } else { - let origin_runtime = RuntimeSpec::from_runtimes( - chunk_graph.get_module_runtimes_iter(*origin_module, chunk_by_ukey), - ); + let origin_runtime = + RuntimeSpec::from_runtimes(chunk_graph.get_module_runtimes_iter(*origin_module)); !runtime.is_disjoint(&origin_runtime) }; @@ -617,9 +620,8 @@ impl ModuleConcatenationPlugin { { !runtime.is_disjoint(origin_runtime) } else { - let origin_runtime = RuntimeSpec::from_runtimes( - chunk_graph.get_module_runtimes_iter(*origin_module, chunk_by_ukey), - ); + let origin_runtime = + RuntimeSpec::from_runtimes(chunk_graph.get_module_runtimes_iter(*origin_module)); !runtime.is_disjoint(&origin_runtime) }; @@ -1144,7 +1146,8 @@ impl ModuleConcatenationPlugin { let runtime = RuntimeSpec::from_runtimes(chunks.iter().map(|chunk| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk) .runtime() })); diff --git a/crates/rspack_plugin_javascript/src/plugin/runtime_context.rs b/crates/rspack_plugin_javascript/src/plugin/runtime_context.rs index 17ff02f574fb..aafd622a9cec 100644 --- a/crates/rspack_plugin_javascript/src/plugin/runtime_context.rs +++ b/crates/rspack_plugin_javascript/src/plugin/runtime_context.rs @@ -145,7 +145,8 @@ var module = ({module_cache}[moduleId] = {{"#, .unwrap_or_default(); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let module_factories = runtime_requirements.contains(RuntimeGlobals::MODULE_FACTORIES); let require_function = runtime_requirements.contains(RuntimeGlobals::REQUIRE); @@ -345,7 +346,8 @@ function {}(moduleId) {{ .map(|chunk_ukey| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey) .expect_id() .to_string() @@ -375,10 +377,7 @@ function {}(moduleId) {{ }) && compilation .build_chunk_graph_artifact .chunk_graph - .get_module_runtimes_iter( - *origin_module, - &compilation.build_chunk_graph_artifact.chunk_by_ukey, - ) + .get_module_runtimes_iter(*origin_module) .any(|runtime| runtime.intersection(chunk.runtime()).count() > 0) }) } { @@ -625,7 +624,8 @@ impl JsPlugin { .expect("should have js plugin drive"); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let supports_arrow_function = compilation .options diff --git a/crates/rspack_plugin_javascript/src/plugin/url_plugin.rs b/crates/rspack_plugin_javascript/src/plugin/url_plugin.rs index 3604f81f3a54..ae4d7c63985e 100644 --- a/crates/rspack_plugin_javascript/src/plugin/url_plugin.rs +++ b/crates/rspack_plugin_javascript/src/plugin/url_plugin.rs @@ -28,7 +28,8 @@ pub struct URLPlugin {} async fn get_chunk_output_path(compilation: &Compilation, chunk_ukey: ChunkUkey) -> Result { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); let filename_template = get_js_chunk_filename_template( chunk, @@ -213,7 +214,8 @@ async fn render_module_content( ) -> Result<()> { let runtime = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey) .runtime(); let codegen_result = compilation diff --git a/crates/rspack_plugin_javascript/src/runtime.rs b/crates/rspack_plugin_javascript/src/runtime.rs index 6b8afd8b1914..cb685832a364 100644 --- a/crates/rspack_plugin_javascript/src/runtime.rs +++ b/crates/rspack_plugin_javascript/src/runtime.rs @@ -137,7 +137,8 @@ pub async fn render_module( let module_identifier = module.identifier(); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let code_gen_result = compilation .code_generation_results @@ -380,7 +381,8 @@ pub async fn render_chunk_runtime_modules( if compilation.options.experiments.runtime_mode == RuntimeMode::Rspack { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if matches!(chunk.kind(), ChunkKind::HotUpdate) { crate::runtime_context::render_hot_update_chunk_runtime_modules( diff --git a/crates/rspack_plugin_javascript/src/runtime_context.rs b/crates/rspack_plugin_javascript/src/runtime_context.rs index f4956c0f7ec5..6b0935a5a553 100644 --- a/crates/rspack_plugin_javascript/src/runtime_context.rs +++ b/crates/rspack_plugin_javascript/src/runtime_context.rs @@ -53,7 +53,8 @@ pub fn should_export_rspack_runtime_globals( let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let module_graph = compilation.get_module_graph(); chunk @@ -620,7 +621,8 @@ pub async fn render_rspack_runtime_modules( ) -> Result { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if matches!(chunk.kind(), ChunkKind::HotUpdate) { render_hot_update_chunk_runtime_modules(compilation, chunk_ukey, runtime_template).await diff --git a/crates/rspack_plugin_library/src/amd_library_plugin.rs b/crates/rspack_plugin_library/src/amd_library_plugin.rs index b9347c1051fe..a7de0786b73b 100644 --- a/crates/rspack_plugin_library/src/amd_library_plugin.rs +++ b/crates/rspack_plugin_library/src/amd_library_plugin.rs @@ -97,7 +97,8 @@ async fn render( }; let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let module_graph = compilation.get_module_graph(); let modules = compilation diff --git a/crates/rspack_plugin_library/src/assign_library_plugin.rs b/crates/rspack_plugin_library/src/assign_library_plugin.rs index 452216727fdb..29cd7b26a161 100644 --- a/crates/rspack_plugin_library/src/assign_library_plugin.rs +++ b/crates/rspack_plugin_library/src/assign_library_plugin.rs @@ -220,7 +220,8 @@ async fn render( if self.options.declare { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let base = &self .get_resolved_full_name(&options, compilation, chunk) @@ -256,7 +257,8 @@ async fn render_startup( source.add(render_source.source.clone()); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let full_name_resolved = self .get_resolved_full_name(&options, compilation, chunk) @@ -363,7 +365,8 @@ async fn js_chunk_hash( PLUGIN_NAME.hash(hasher); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let full_resolved_name = self .get_resolved_full_name(&options, compilation, chunk) diff --git a/crates/rspack_plugin_library/src/jsonp_library_plugin.rs b/crates/rspack_plugin_library/src/jsonp_library_plugin.rs index 4d3e9274f216..51e9fbff2b6f 100644 --- a/crates/rspack_plugin_library/src/jsonp_library_plugin.rs +++ b/crates/rspack_plugin_library/src/jsonp_library_plugin.rs @@ -80,7 +80,8 @@ async fn render( }; let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let name = compilation .get_path( diff --git a/crates/rspack_plugin_library/src/module_library_plugin.rs b/crates/rspack_plugin_library/src/module_library_plugin.rs index dbb54a40f4bc..700926a2f2ff 100644 --- a/crates/rspack_plugin_library/src/module_library_plugin.rs +++ b/crates/rspack_plugin_library/src/module_library_plugin.rs @@ -96,7 +96,8 @@ async fn render_startup( let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let info_name = export_info.name().expect("should have name"); let var_name = format!("{exports_name}{}", to_identifier(info_name)); diff --git a/crates/rspack_plugin_library/src/system_library_plugin.rs b/crates/rspack_plugin_library/src/system_library_plugin.rs index a5e2e33f91f1..c1133644e951 100644 --- a/crates/rspack_plugin_library/src/system_library_plugin.rs +++ b/crates/rspack_plugin_library/src/system_library_plugin.rs @@ -91,7 +91,8 @@ async fn render( let name = if let Some(name) = options.name { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_ukey); let filename = Filename::from(name); let path_data = match chunk { diff --git a/crates/rspack_plugin_library/src/umd_library_plugin.rs b/crates/rspack_plugin_library/src/umd_library_plugin.rs index 761068e841b4..799958add258 100644 --- a/crates/rspack_plugin_library/src/umd_library_plugin.rs +++ b/crates/rspack_plugin_library/src/umd_library_plugin.rs @@ -110,7 +110,8 @@ async fn render( .supports_arrow_function(); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let module_graph = compilation.get_module_graph(); let module_graph_cache = &compilation.module_graph_cache_artifact; diff --git a/crates/rspack_plugin_library/src/utils.rs b/crates/rspack_plugin_library/src/utils.rs index 911239135051..8eee573b9de3 100644 --- a/crates/rspack_plugin_library/src/utils.rs +++ b/crates/rspack_plugin_library/src/utils.rs @@ -62,7 +62,8 @@ pub fn get_options_for_chunk<'a>( } let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); chunk .get_entry_options(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) diff --git a/crates/rspack_plugin_limit_chunk_count/src/chunk_combination.rs b/crates/rspack_plugin_limit_chunk_count/src/chunk_combination.rs index e23b35e65a90..19b36a13c5b6 100644 --- a/crates/rspack_plugin_limit_chunk_count/src/chunk_combination.rs +++ b/crates/rspack_plugin_limit_chunk_count/src/chunk_combination.rs @@ -138,18 +138,29 @@ impl ChunkCombinationBucket { #[cfg(test)] mod test { - use rspack_core::ChunkUkey; use super::*; #[test] fn pop_delete_and_update() { - let chunk_0 = ChunkUkey::new(); - let chunk_1 = ChunkUkey::new(); - let chunk_2 = ChunkUkey::new(); - let chunk_3 = ChunkUkey::new(); - let chunk_4 = ChunkUkey::new(); - let chunk_5 = ChunkUkey::new(); + let chunk_0 = rspack_core::ChunkGraph::default() + .create_chunk(None, rspack_core::ChunkKind::Normal) + .expect("test Chunk allocation"); + let chunk_1 = rspack_core::ChunkGraph::default() + .create_chunk(None, rspack_core::ChunkKind::Normal) + .expect("test Chunk allocation"); + let chunk_2 = rspack_core::ChunkGraph::default() + .create_chunk(None, rspack_core::ChunkKind::Normal) + .expect("test Chunk allocation"); + let chunk_3 = rspack_core::ChunkGraph::default() + .create_chunk(None, rspack_core::ChunkKind::Normal) + .expect("test Chunk allocation"); + let chunk_4 = rspack_core::ChunkGraph::default() + .create_chunk(None, rspack_core::ChunkKind::Normal) + .expect("test Chunk allocation"); + let chunk_5 = rspack_core::ChunkGraph::default() + .create_chunk(None, rspack_core::ChunkKind::Normal) + .expect("test Chunk allocation"); let mut combinations = ChunkCombinationBucket::new(); diff --git a/crates/rspack_plugin_limit_chunk_count/src/lib.rs b/crates/rspack_plugin_limit_chunk_count/src/lib.rs index 099839c38ef4..07dc0a830096 100644 --- a/crates/rspack_plugin_limit_chunk_count/src/lib.rs +++ b/crates/rspack_plugin_limit_chunk_count/src/lib.rs @@ -2,33 +2,12 @@ mod chunk_combination; use chunk_combination::{ChunkCombination, ChunkCombinationBucket, ChunkCombinationUkey}; use rspack_core::{ - ChunkSizeOptions, ChunkUkey, Compilation, CompilationOptimizeChunks, Plugin, + ChunkMap, ChunkSizeOptions, ChunkUkey, Compilation, CompilationOptimizeChunks, Plugin, compare_chunks_with_graph, incremental::Mutation, }; use rspack_error::Result; use rspack_hook::{plugin, plugin_hook}; -use rustc_hash::{FxHashMap, FxHashSet}; - -fn add_to_set_map( - map: &mut FxHashMap>, - key: &ChunkUkey, - value: ChunkCombinationUkey, -) { - if map.get(key).is_none() { - let mut set = FxHashSet::default(); - set.insert(value); - map.insert(*key, set); - } else { - let set = map.get_mut(key); - if let Some(set) = set { - set.insert(value); - } else { - let mut set = FxHashSet::default(); - set.insert(value); - map.insert(*key, set); - } - } -} +use rustc_hash::FxHashSet; #[derive(Debug, Clone, Default)] pub struct LimitChunkCountPluginOptions { @@ -61,7 +40,8 @@ async fn optimize_chunks(&self, compilation: &mut Compilation) -> Result>(); @@ -69,20 +49,16 @@ async fn optimize_chunks(&self, compilation: &mut Compilation) -> Result Result> = - FxHashMap::default(); + let mut combinations_by_chunk: ChunkMap> = + ChunkMap::with_capacity(chunk_by_ukey.slot_count()); let chunk_size_option = ChunkSizeOptions { chunk_overhead: self.options.chunk_overhead, @@ -107,7 +83,7 @@ async fn optimize_chunks(&self, compilation: &mut Compilation) -> Result Result Result Result Result Result Result Result Result Result Result Result>(); @@ -25,7 +26,8 @@ async fn optimize_chunks(&self, compilation: &mut Compilation) -> Result Result Result Result Result Result<()> { let Some(entry_options) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_ukey) .and_then(|chunk| { chunk.get_entry_options(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) diff --git a/crates/rspack_plugin_mf/src/container/embed_federation_runtime_module.rs b/crates/rspack_plugin_mf/src/container/embed_federation_runtime_module.rs index 20e639fec6b4..1041f5448c6a 100644 --- a/crates/rspack_plugin_mf/src/container/embed_federation_runtime_module.rs +++ b/crates/rspack_plugin_mf/src/container/embed_federation_runtime_module.rs @@ -137,14 +137,16 @@ impl RuntimeModule for EmbedFederationRuntimeModule { if self.options.experiments.async_startup { let entry_chunk_ids = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey) .get_all_initial_chunks(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) .into_iter() .map(|chunk_ukey| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey) .expect_id() .clone() diff --git a/crates/rspack_plugin_mf/src/container/embed_federation_runtime_plugin.rs b/crates/rspack_plugin_mf/src/container/embed_federation_runtime_plugin.rs index bcd14563c489..93f4a0270c79 100644 --- a/crates/rspack_plugin_mf/src/container/embed_federation_runtime_plugin.rs +++ b/crates/rspack_plugin_mf/src/container/embed_federation_runtime_plugin.rs @@ -65,7 +65,8 @@ async fn additional_chunk_runtime_requirements_tree( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); // Skip build time chunks @@ -109,7 +110,8 @@ async fn runtime_requirement_in_tree( ) -> Result> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); // Skip build time chunks @@ -187,7 +189,8 @@ async fn render_startup( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); // Skip build time chunks diff --git a/crates/rspack_plugin_mf/src/container/expose_runtime_module.rs b/crates/rspack_plugin_mf/src/container/expose_runtime_module.rs index c890b6f56250..2230a735ea4c 100644 --- a/crates/rspack_plugin_mf/src/container/expose_runtime_module.rs +++ b/crates/rspack_plugin_mf/src/container/expose_runtime_module.rs @@ -25,7 +25,8 @@ impl ExposeRuntimeModule { ) -> Option<&'a CodeGenerationDataExpose> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let module_graph = compilation.get_module_graph(); for c in @@ -33,7 +34,8 @@ impl ExposeRuntimeModule { { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&c); let modules = compilation .build_chunk_graph_artifact diff --git a/crates/rspack_plugin_mf/src/container/federation_data_runtime_module.rs b/crates/rspack_plugin_mf/src/container/federation_data_runtime_module.rs index e68ed25071a9..296866e05648 100644 --- a/crates/rspack_plugin_mf/src/container/federation_data_runtime_module.rs +++ b/crates/rspack_plugin_mf/src/container/federation_data_runtime_module.rs @@ -39,7 +39,8 @@ impl RuntimeModule for FederationDataRuntimeModule { let runtime_template = context.runtime_template; let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&self.chunk().expect("The chunk should be attached.")); Ok(federation_runtime_template(chunk, runtime_template, compilation).await) } diff --git a/crates/rspack_plugin_mf/src/container/hoist_container_references_plugin.rs b/crates/rspack_plugin_mf/src/container/hoist_container_references_plugin.rs index 025873bd85fb..89f721383837 100644 --- a/crates/rspack_plugin_mf/src/container/hoist_container_references_plugin.rs +++ b/crates/rspack_plugin_mf/src/container/hoist_container_references_plugin.rs @@ -199,7 +199,8 @@ async fn optimize_chunks(&self, compilation: &mut Compilation) -> Result Result>(); @@ -260,30 +258,11 @@ async fn optimize_chunks(&self, compilation: &mut Compilation) -> Result::generate"); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); let mut chunk_to_remotes_mapping = FxHashMap::default(); let mut id_to_remote_data_mapping = FxHashMap::default(); @@ -126,7 +127,8 @@ impl RuntimeModule for RemoteRuntimeModule { } let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk); chunk_to_remotes_mapping.insert( chunk diff --git a/crates/rspack_plugin_mf/src/manifest/asset.rs b/crates/rspack_plugin_mf/src/manifest/asset.rs index 04849a9d8077..1e7443927e84 100644 --- a/crates/rspack_plugin_mf/src/manifest/asset.rs +++ b/crates/rspack_plugin_mf/src/manifest/asset.rs @@ -19,7 +19,8 @@ pub fn collect_assets_from_chunk( let mut css_async = HashSet::::default(); let Some(chunk) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_key) else { return empty_assets_group(); @@ -45,7 +46,8 @@ pub fn collect_assets_from_chunk( for chunk_ukey in &group.chunks { let group_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if let Some(group_chunk_name) = group_chunk.name() && let Some(chunk_name) = chunk.name() @@ -70,7 +72,8 @@ pub fn collect_assets_from_chunk( { let async_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&async_chunk_key); for file in async_chunk.files() { if file.ends_with(".css") { @@ -88,7 +91,7 @@ pub fn collect_assets_from_chunk( .name() .is_some_and(|name| entry_point_names.contains(name)); if !skip { - for file in group.get_files(&compilation.build_chunk_graph_artifact.chunk_by_ukey) { + for file in group.get_files(&compilation.build_chunk_graph_artifact.chunk_graph.chunks) { if file.ends_with(".css") { css_async.insert(file.clone()); } else if !is_hot_file(&file) { diff --git a/crates/rspack_plugin_mf/src/manifest/mod.rs b/crates/rspack_plugin_mf/src/manifest/mod.rs index bb3054348e03..5d98860181ba 100644 --- a/crates/rspack_plugin_mf/src/manifest/mod.rs +++ b/crates/rspack_plugin_mf/src/manifest/mod.rs @@ -60,7 +60,8 @@ fn get_remote_entry_name(compilation: &Compilation, container_name: &str) -> Opt let pick_chunk_file = |chunk_ukey: &rspack_core::ChunkUkey| -> Option { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); chunk .files() @@ -371,7 +372,8 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { chunk_group.chunks.iter().find(|chunk_key| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_key) .and_then(|chunk| chunk.name()) .is_some_and(|name| candidates.iter().any(|candidate| candidate == name)) @@ -384,7 +386,8 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { if let Some(chunk_key) = chunk_group.chunks.iter().find(|chunk_key| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_key) .and_then(|chunk| chunk.name()) .is_some() @@ -514,7 +517,8 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { entry.insert(*chunk_ukey); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); for group_ukey in chunk.groups() { let group = compilation @@ -586,7 +590,8 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { for chunk_key in chunk_keys { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_key); // A provided module can also be imported locally into an expose chunk. // Protect the same chunks used to collect assets, including unnamed ones. diff --git a/crates/rspack_plugin_mf/src/manifest/utils.rs b/crates/rspack_plugin_mf/src/manifest/utils.rs index a84240e3442f..36eb74b02eaa 100644 --- a/crates/rspack_plugin_mf/src/manifest/utils.rs +++ b/crates/rspack_plugin_mf/src/manifest/utils.rs @@ -47,7 +47,8 @@ pub fn collect_entry_files(compilation: &Compilation, container_name: &str) -> H for chunk_ukey in &entrypoint.chunks { if let Some(chunk) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_ukey) { for file in chunk.files() { @@ -58,7 +59,8 @@ pub fn collect_entry_files(compilation: &Compilation, container_name: &str) -> H { if let Some(async_chunk) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(&async_chunk_ukey) { let mut should_filter = false; @@ -88,7 +90,8 @@ pub fn collect_entry_files(compilation: &Compilation, container_name: &str) -> H entrypoint.get_runtime_chunk(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey); if let Some(chunk) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(&runtime_chunk_ukey) { for file in chunk.files() { diff --git a/crates/rspack_plugin_mf/src/sharing/consume_shared_runtime_module.rs b/crates/rspack_plugin_mf/src/sharing/consume_shared_runtime_module.rs index b77d1f9bcf0a..38a71e88eaa5 100644 --- a/crates/rspack_plugin_mf/src/sharing/consume_shared_runtime_module.rs +++ b/crates/rspack_plugin_mf/src/sharing/consume_shared_runtime_module.rs @@ -112,7 +112,8 @@ impl RuntimeModule for ConsumeSharedRuntimeModule { .expect("should have chunk in ::generate"); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); let module_graph = compilation.get_module_graph(); let mut chunk_to_module_mapping = BTreeMap::default(); @@ -168,7 +169,8 @@ impl RuntimeModule for ConsumeSharedRuntimeModule { modules.sort_unstable(); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk); let mut ids = vec![]; for mid in modules { @@ -199,7 +201,8 @@ impl RuntimeModule for ConsumeSharedRuntimeModule { modules.sort_unstable(); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk); for mid in modules { add_module(mid, chunk, &mut initial_consumes); diff --git a/crates/rspack_plugin_mf/src/sharing/share_runtime_module.rs b/crates/rspack_plugin_mf/src/sharing/share_runtime_module.rs index f79f6f2af99f..d38cd4c6c87c 100644 --- a/crates/rspack_plugin_mf/src/sharing/share_runtime_module.rs +++ b/crates/rspack_plugin_mf/src/sharing/share_runtime_module.rs @@ -82,7 +82,8 @@ impl RuntimeModule for ShareRuntimeModule { .expect("should have chunk in ::generate"); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); let module_graph = compilation.get_module_graph(); let mut init_per_scope: FxHashMap< @@ -94,7 +95,8 @@ impl RuntimeModule for ShareRuntimeModule { { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&c); let mut modules = compilation .build_chunk_graph_artifact diff --git a/crates/rspack_plugin_mf/src/sharing/shared_container_plugin.rs b/crates/rspack_plugin_mf/src/sharing/shared_container_plugin.rs index 86d5f99c0e2e..3184cd499625 100644 --- a/crates/rspack_plugin_mf/src/sharing/shared_container_plugin.rs +++ b/crates/rspack_plugin_mf/src/sharing/shared_container_plugin.rs @@ -86,7 +86,8 @@ async fn additional_tree_runtime_requirements( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if let Some(name) = chunk.name() && name == self.options.name diff --git a/crates/rspack_plugin_module_info_header/src/lib.rs b/crates/rspack_plugin_module_info_header/src/lib.rs index 2df1c780fee0..eaa98331d8fc 100644 --- a/crates/rspack_plugin_module_info_header/src/lib.rs +++ b/crates/rspack_plugin_module_info_header/src/lib.rs @@ -281,7 +281,8 @@ async fn render_js_module_package( let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_key) .expect("Chunk must exists"); diff --git a/crates/rspack_plugin_progress/src/lib.rs b/crates/rspack_plugin_progress/src/lib.rs index f4869be1afaa..10d567eb43a7 100644 --- a/crates/rspack_plugin_progress/src/lib.rs +++ b/crates/rspack_plugin_progress/src/lib.rs @@ -12,16 +12,16 @@ use futures::future::BoxFuture; use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget, ProgressStyle}; use rspack_collections::IdentifierMap; use rspack_core::{ - AsyncModulesArtifact, BoxModule, ChunkByUkey, ChunkNamedIdArtifact, CircularModulesInfo, - Compilation, CompilationAfterOptimizeModules, CompilationAfterProcessAssets, - CompilationBuildModule, CompilationChunkIds, CompilationFinishModules, CompilationId, - CompilationModuleIds, CompilationOptimizeChunkModules, CompilationOptimizeChunks, - CompilationOptimizeCodeGeneration, CompilationOptimizeDependencies, CompilationOptimizeModules, - CompilationOptimizeTree, CompilationParams, CompilationProcessAssets, CompilationSeal, - CompilationSucceedModule, CompilerAfterEmit, CompilerClose, CompilerCompilation, CompilerEmit, - CompilerFinishMake, CompilerId, CompilerMake, CompilerThisCompilation, ExportsInfoArtifact, - ModuleIdentifier, ModuleIdsArtifact, Plugin, SideEffectsOptimizeArtifact, - SideEffectsStateArtifact, build_module_graph::BuildModuleGraphArtifact, + AsyncModulesArtifact, BoxModule, CircularModulesInfo, Compilation, + CompilationAfterOptimizeModules, CompilationAfterProcessAssets, CompilationBuildModule, + CompilationChunkIds, CompilationFinishModules, CompilationId, CompilationModuleIds, + CompilationOptimizeChunkModules, CompilationOptimizeChunks, CompilationOptimizeCodeGeneration, + CompilationOptimizeDependencies, CompilationOptimizeModules, CompilationOptimizeTree, + CompilationParams, CompilationProcessAssets, CompilationSeal, CompilationSucceedModule, + CompilerAfterEmit, CompilerClose, CompilerCompilation, CompilerEmit, CompilerFinishMake, + CompilerId, CompilerMake, CompilerThisCompilation, ExportsInfoArtifact, ModuleIdentifier, + ModuleIdsArtifact, Plugin, SideEffectsOptimizeArtifact, SideEffectsStateArtifact, + build_module_graph::BuildModuleGraphArtifact, }; use rspack_error::{Diagnostic, Result}; use rspack_hook::{plugin, plugin_hook}; @@ -514,13 +514,7 @@ async fn module_ids( } #[plugin_hook(CompilationChunkIds for ProgressPlugin)] -async fn chunk_ids( - &self, - _compilation: &Compilation, - _chunk_by_ukey: &mut ChunkByUkey, - _named_chunk_ids_artifact: &mut ChunkNamedIdArtifact, - _diagnostics: &mut Vec, -) -> Result<()> { +async fn chunk_ids(&self, _compilation: &mut Compilation) -> Result<()> { self.sealing_hooks_report("assign chunk ids", 21).await } diff --git a/crates/rspack_plugin_remove_duplicate_modules/src/lib.rs b/crates/rspack_plugin_remove_duplicate_modules/src/lib.rs index 7bf643d59cde..43afd2d06c9f 100644 --- a/crates/rspack_plugin_remove_duplicate_modules/src/lib.rs +++ b/crates/rspack_plugin_remove_duplicate_modules/src/lib.rs @@ -89,7 +89,8 @@ fn move_empty_anonymous_non_initial_entrypoints( let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if chunk.name().is_some() || chunk.filename_template().is_some() { continue; @@ -107,7 +108,7 @@ fn move_empty_anonymous_non_initial_entrypoints( if modules.contains(module) && chunk_group.kind.is_entrypoint() && !chunk_group.is_initial() - && chunk_group.get_entrypoint_chunk() == *chunk_ukey + && chunk_group.is_entrypoint_chunk(chunk_ukey) { entrypoints.push((*chunk_ukey, *module, *group)); } @@ -122,6 +123,7 @@ fn move_empty_anonymous_non_initial_entrypoints( compilation .build_chunk_graph_artifact .chunk_graph + .topology .connect_chunk_and_entry_module(new_chunk_ukey, module, group); compilation .build_chunk_graph_artifact @@ -196,8 +198,10 @@ async fn optimize_chunks(&self, compilation: &mut Compilation) -> Result Result Result Result Result>(); for chunk_ukey in empty_chunks.iter() { - if let Some(mut chunk) = compilation + if compilation .build_chunk_graph_artifact - .chunk_by_ukey - .remove(chunk_ukey) + .remove_chunk(chunk_ukey) + .is_some() { - chunk_graph.disconnect_chunk( - &mut chunk, - &mut compilation.build_chunk_graph_artifact.chunk_group_by_ukey, - ); if let Some(mut mutations) = compilation.incremental.mutations_write() { mutations.add(Mutation::ChunkRemove { chunk: *chunk_ukey }); } diff --git a/crates/rspack_plugin_rsc/src/client_plugin.rs b/crates/rspack_plugin_rsc/src/client_plugin.rs index 6bed471becdc..a6bd69706f05 100644 --- a/crates/rspack_plugin_rsc/src/client_plugin.rs +++ b/crates/rspack_plugin_rsc/src/client_plugin.rs @@ -45,7 +45,8 @@ fn extend_required_chunks( for chunk_ukey in &chunk_group.chunks { let Some(chunk) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_ukey) else { continue; @@ -103,7 +104,8 @@ fn record_module( let is_async = ModuleGraph::is_async(&compilation.async_modules_artifact, module_identifier); let css_files: Vec = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_ukey) .map(|chunk| { let prefix = &module_loading.prefix; @@ -141,7 +143,7 @@ fn collect_css_files_from_chunks<'a>( compilation: &Compilation, ) -> Vec { let prefix = &module_loading.prefix; - let chunk_by_ukey = &compilation.build_chunk_graph_artifact.chunk_by_ukey; + let chunk_by_ukey = &compilation.build_chunk_graph_artifact.chunk_graph.chunks; chunk_ukeys .into_iter() .filter_map(|chunk_ukey| chunk_by_ukey.get(chunk_ukey)) @@ -365,7 +367,7 @@ fn collect_bootstrap_scripts( .prefix; let bootstrap_scripts = chunk_group - .get_files(&compilation.build_chunk_graph_artifact.chunk_by_ukey) + .get_files(&compilation.build_chunk_graph_artifact.chunk_graph.chunks) .into_iter() .filter(|chunk_file| chunk_file.ends_with(".js")) .filter(|chunk_file| { diff --git a/crates/rspack_plugin_rsc/src/manifest_runtime_module.rs b/crates/rspack_plugin_rsc/src/manifest_runtime_module.rs index e7f584103069..b9c7b39d9c0b 100644 --- a/crates/rspack_plugin_rsc/src/manifest_runtime_module.rs +++ b/crates/rspack_plugin_rsc/src/manifest_runtime_module.rs @@ -55,7 +55,8 @@ impl RuntimeModule for RscManifestRuntimeModule { .and_then(|chunk_ukey| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_ukey) }) .and_then(|chunk| { diff --git a/crates/rspack_plugin_rsc/src/server_plugin.rs b/crates/rspack_plugin_rsc/src/server_plugin.rs index 14b5ff31c235..b764a72d8314 100644 --- a/crates/rspack_plugin_rsc/src/server_plugin.rs +++ b/crates/rspack_plugin_rsc/src/server_plugin.rs @@ -7,13 +7,13 @@ use derive_more::Debug; use futures::future::BoxFuture; use rspack_collections::{Identifiable, IdentifierMap}; use rspack_core::{ - BoxDependency, ChunkByUkey, ChunkNamedIdArtifact, ChunkUkey, Compilation, CompilationChunkIds, - CompilationParams, CompilationRuntimeRequirementInTree, CompilerCompilation, CompilerDone, - CompilerFailed, CompilerFinishMake, CompilerThisCompilation, Dependency, DependencyId, - DependencyType, EntryDependency, EntryOptions, Logger, Plugin, RuntimeGlobals, RuntimeModule, - RuntimeSpec, get_entry_runtime, + BoxDependency, ChunkUkey, Compilation, CompilationChunkIds, CompilationParams, + CompilationRuntimeRequirementInTree, CompilerCompilation, CompilerDone, CompilerFailed, + CompilerFinishMake, CompilerThisCompilation, Dependency, DependencyId, DependencyType, + EntryDependency, EntryOptions, Logger, Plugin, RuntimeGlobals, RuntimeModule, RuntimeSpec, + get_entry_runtime, }; -use rspack_error::{Diagnostic, Result, ToStringResultToRspackResultExt}; +use rspack_error::{Result, ToStringResultToRspackResultExt}; use rspack_hook::{plugin, plugin_hook}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -278,13 +278,7 @@ async fn runtime_requirements_in_tree( /// Compute server manifest and server_consumer_module_map once per entry. Stored in plugin_state for /// RscManifestRuntimeModule and onManifest to avoid recomputing. #[plugin_hook(CompilationChunkIds for RscServerPlugin, stage = -10000)] -async fn chunk_ids( - &self, - compilation: &Compilation, - _chunk_by_ukey: &mut ChunkByUkey, - _named_chunk_ids_artifact: &mut ChunkNamedIdArtifact, - _diagnostics: &mut Vec, -) -> Result<()> { +async fn chunk_ids(&self, compilation: &mut Compilation) -> Result<()> { let mut plugin_state = PLUGIN_STATES.entry(compilation.compiler_id()).or_default(); build_server_manifest(compilation, &mut plugin_state)?; for (_entry_name, entry_state) in plugin_state.entries.iter_mut() { diff --git a/crates/rspack_plugin_rsdoctor/src/chunk_graph.rs b/crates/rspack_plugin_rsdoctor/src/chunk_graph.rs index 04eb0e1fd6b2..e810ba513317 100644 --- a/crates/rspack_plugin_rsdoctor/src/chunk_graph.rs +++ b/crates/rspack_plugin_rsdoctor/src/chunk_graph.rs @@ -3,7 +3,7 @@ use std::sync::{Arc, atomic::AtomicI32}; use rayon::iter::{IntoParallelRefIterator, ParallelBridge, ParallelIterator}; use rspack_collections::IdentifierMap; use rspack_core::{ - Chunk, ChunkByUkey, ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey, ChunkUkey, CompilationAsset, + Chunk, ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey, ChunkSlotMap, ChunkUkey, CompilationAsset, ModuleGraph, }; use rspack_util::fx_hash::{FxHashMap, FxIndexMap}; @@ -57,7 +57,7 @@ pub fn collect_chunk_dependencies( chunks: &HashMap, rsd_chunks: &HashMap, chunk_group_by_ukey: &ChunkGroupByUkey, - chunk_by_ukey: &ChunkByUkey, + chunk_by_ukey: &ChunkSlotMap, ) -> HashMap, HashSet)> { chunks .par_iter() @@ -138,7 +138,7 @@ pub fn collect_entrypoints( pub fn collect_assets( assets: &HashMap, - chunk_by_ukey: &ChunkByUkey, + chunk_by_ukey: &ChunkSlotMap, ) -> HashMap { let asset_ukey_counter: Arc = Arc::new(AtomicI32::new(0)); let mut compilation_file_to_chunks: HashMap<&String, Vec<&ChunkUkey>> = HashMap::default(); @@ -177,7 +177,7 @@ pub fn collect_assets( } pub fn collect_chunk_modules( - chunk_by_ukey: &ChunkByUkey, + chunk_by_ukey: &ChunkSlotMap, module_ukeys: &IdentifierMap, chunk_graph: &ChunkGraph, module_graph: &ModuleGraph, @@ -217,7 +217,7 @@ pub fn collect_chunk_modules( } pub fn collect_chunk_assets( - chunk_by_ukey: &ChunkByUkey, + chunk_by_ukey: &ChunkSlotMap, rsd_assets: &HashMap, ) -> Vec { chunk_by_ukey @@ -239,7 +239,7 @@ pub fn collect_entrypoint_assets( rsd_assets: &FxHashMap, entrypoint_ukey_map: &HashMap, chunk_group_by_ukey: &ChunkGroupByUkey, - chunk_by_ukey: &ChunkByUkey, + chunk_by_ukey: &ChunkSlotMap, ) -> Vec { entrypoints .par_iter() diff --git a/crates/rspack_plugin_rsdoctor/src/plugin.rs b/crates/rspack_plugin_rsdoctor/src/plugin.rs index 41abe65ef323..a33bc842a7bb 100644 --- a/crates/rspack_plugin_rsdoctor/src/plugin.rs +++ b/crates/rspack_plugin_rsdoctor/src/plugin.rs @@ -291,7 +291,7 @@ async fn optimize_chunks(&self, compilation: &mut Compilation) -> Result Result< let mut rsd_dependencies = HashMap::default(); let chunk_graph = &compilation.build_chunk_graph_artifact.chunk_graph; - let chunk_by_ukey = &compilation.build_chunk_graph_artifact.chunk_by_ukey; + let chunk_by_ukey = &compilation.build_chunk_graph_artifact.chunk_graph.chunks; let modules = module_graph .modules() .map(|(id, module)| (*id, module)) @@ -622,7 +622,7 @@ async fn after_process_assets( let hooks = RsdoctorPlugin::get_compilation_hooks(compilation.id()); - let chunk_by_ukey = &compilation.build_chunk_graph_artifact.chunk_by_ukey; + let chunk_by_ukey = &compilation.build_chunk_graph_artifact.chunk_graph.chunks; let chunk_group_by_ukey = &compilation.build_chunk_graph_artifact.chunk_group_by_ukey; let rsd_assets = collect_assets(compilation.assets(), chunk_by_ukey); let rsd_chunk_assets = collect_chunk_assets(chunk_by_ukey, &rsd_assets); diff --git a/crates/rspack_plugin_rstest/src/plugin.rs b/crates/rspack_plugin_rstest/src/plugin.rs index b2c0867d0ff4..dfa025805bb7 100644 --- a/crates/rspack_plugin_rstest/src/plugin.rs +++ b/crates/rspack_plugin_rstest/src/plugin.rs @@ -599,11 +599,18 @@ struct MockFlagPos { #[plugin_hook(CompilationProcessAssets for RstestPlugin, stage = Compilation::PROCESS_ASSETS_STAGE_ADDITIONAL)] async fn mock_hoist_process_assets(&self, compilation: &mut Compilation) -> Result<()> { - let mut files = Vec::with_capacity(compilation.build_chunk_graph_artifact.chunk_by_ukey.len()); + let mut files = Vec::with_capacity( + compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .len(), + ); for chunk in compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .values() { for file in chunk.files() { diff --git a/crates/rspack_plugin_runtime/src/array_push_callback_chunk_format.rs b/crates/rspack_plugin_runtime/src/array_push_callback_chunk_format.rs index 82f69c60176d..c4e8a76c6f2b 100644 --- a/crates/rspack_plugin_runtime/src/array_push_callback_chunk_format.rs +++ b/crates/rspack_plugin_runtime/src/array_push_callback_chunk_format.rs @@ -43,7 +43,8 @@ async fn additional_chunk_runtime_requirements( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if chunk.has_runtime(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) { @@ -74,7 +75,8 @@ async fn js_chunk_hash( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if chunk.has_runtime(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) { return Ok(()); @@ -111,7 +113,8 @@ async fn render_chunk( let hooks = JsPlugin::get_compilation_hooks(compilation.id()); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let has_runtime_modules = compilation .build_chunk_graph_artifact diff --git a/crates/rspack_plugin_runtime/src/chunk_prefetch_preload.rs b/crates/rspack_plugin_runtime/src/chunk_prefetch_preload.rs index e834546c6419..a7d321e20f05 100644 --- a/crates/rspack_plugin_runtime/src/chunk_prefetch_preload.rs +++ b/crates/rspack_plugin_runtime/src/chunk_prefetch_preload.rs @@ -24,7 +24,8 @@ async fn additional_chunk_runtime_requirements( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if compilation .build_chunk_graph_artifact @@ -56,7 +57,8 @@ async fn additional_tree_runtime_requirements( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let chunk_filter = |_: &ChunkUkey, __: &Compilation| true; let mut chunk_map = chunk.get_child_ids_by_orders_map(false, compilation, &chunk_filter); diff --git a/crates/rspack_plugin_runtime/src/common_js_chunk_format.rs b/crates/rspack_plugin_runtime/src/common_js_chunk_format.rs index 84d91acf1d64..8aa364841716 100644 --- a/crates/rspack_plugin_runtime/src/common_js_chunk_format.rs +++ b/crates/rspack_plugin_runtime/src/common_js_chunk_format.rs @@ -47,7 +47,8 @@ async fn additional_chunk_runtime_requirements( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if chunk.has_runtime(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) { @@ -77,7 +78,8 @@ async fn js_chunk_hash( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if chunk.has_runtime(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) { return Ok(()); @@ -107,7 +109,8 @@ fn compilation_dependent_full_hash( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if chunk.has_entry_module(&compilation.build_chunk_graph_artifact.chunk_graph) && runtime_chunk_has_hash(compilation, chunk_ukey)? @@ -128,7 +131,8 @@ async fn render_chunk( let hooks = JsPlugin::get_compilation_hooks(compilation.id()); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let base_chunk_output_name = get_chunk_output_name(chunk, compilation).await?; let mut sources = ConcatSource::default(); diff --git a/crates/rspack_plugin_runtime/src/helpers.rs b/crates/rspack_plugin_runtime/src/helpers.rs index 8b17d23c5cd3..e79f366fe4a7 100644 --- a/crates/rspack_plugin_runtime/src/helpers.rs +++ b/crates/rspack_plugin_runtime/src/helpers.rs @@ -65,7 +65,8 @@ pub fn update_hash_for_entry_startup( ) { if let Some(chunk) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(&chunk_ukey) { chunk.id().hash(hasher); @@ -161,7 +162,8 @@ pub async fn get_runtime_chunk_output_name( let runtime_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get( &entry_point.get_runtime_chunk(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey), ); @@ -191,7 +193,8 @@ pub fn runtime_chunk_has_hash(compilation: &Compilation, chunk_ukey: &ChunkUkey) entry_point.get_runtime_chunk(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey); let runtime_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&runtime_chunk_ukey); let filename = get_js_chunk_filename_template( @@ -266,7 +269,8 @@ pub fn generate_entry_startup( .map(|chunk_ukey| { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); chunk.expect_id().clone() }) diff --git a/crates/rspack_plugin_runtime/src/module_chunk_format.rs b/crates/rspack_plugin_runtime/src/module_chunk_format.rs index 6678038695f5..1f9531accc4b 100644 --- a/crates/rspack_plugin_runtime/src/module_chunk_format.rs +++ b/crates/rspack_plugin_runtime/src/module_chunk_format.rs @@ -51,7 +51,8 @@ async fn additional_chunk_runtime_requirements( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if chunk.has_runtime(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) { @@ -80,7 +81,8 @@ async fn js_chunk_hash( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if chunk.has_runtime(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) { return Ok(()); @@ -114,7 +116,8 @@ fn compilation_dependent_full_hash( let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); if !chunk.has_entry_module(&compilation.build_chunk_graph_artifact.chunk_graph) { @@ -139,7 +142,8 @@ async fn render_chunk( let hooks = JsPlugin::get_compilation_hooks(compilation.id()); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let base_chunk_output_name = get_chunk_output_name(chunk, compilation).await?; let chunk_runtime_template = compilation.runtime_template.create_chunk_code_template(); @@ -246,7 +250,8 @@ async fn render_chunk( let index = loaded_chunks.len(); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let other_chunk_output_name = get_chunk_output_name(chunk, compilation).await?; let mut index_buffer = itoa::Buffer::new(); @@ -340,7 +345,8 @@ async fn render_startup( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let entries_count = compilation .build_chunk_graph_artifact @@ -354,7 +360,6 @@ async fn render_startup( .chunk_graph .get_chunk_entry_dependent_chunks_iterable( chunk_ukey, - &compilation.build_chunk_graph_artifact.chunk_by_ukey, &compilation.build_chunk_graph_artifact.chunk_group_by_ukey, ); @@ -368,7 +373,8 @@ async fn render_startup( let dependant_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&ck); let named_import = format!("__rspack_imports_{index}"); diff --git a/crates/rspack_plugin_runtime/src/module_chunk_loading.rs b/crates/rspack_plugin_runtime/src/module_chunk_loading.rs index d79ddc481db7..a4743faae5f1 100644 --- a/crates/rspack_plugin_runtime/src/module_chunk_loading.rs +++ b/crates/rspack_plugin_runtime/src/module_chunk_loading.rs @@ -16,7 +16,8 @@ use crate::{ fn is_modern_module_library_chunk(chunk_ukey: &ChunkUkey, compilation: &Compilation) -> bool { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); chunk .get_entry_options(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) diff --git a/crates/rspack_plugin_runtime/src/runtime_module/auto_public_path.rs b/crates/rspack_plugin_runtime/src/runtime_module/auto_public_path.rs index dda820ca324f..0743df249164 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/auto_public_path.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/auto_public_path.rs @@ -45,7 +45,8 @@ impl RuntimeModule for AutoPublicPathRuntimeModule { let chunk = self.chunk().expect("The chunk should be attached"); let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk); let filename = get_js_chunk_filename_template( chunk, diff --git a/crates/rspack_plugin_runtime/src/runtime_module/base_uri.rs b/crates/rspack_plugin_runtime/src/runtime_module/base_uri.rs index 72cd5f80783e..964c52cc1949 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/base_uri.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/base_uri.rs @@ -41,7 +41,8 @@ impl RuntimeModule for BaseUriRuntimeModule { .and_then(|ukey| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(&ukey) }) .and_then(|chunk| { diff --git a/crates/rspack_plugin_runtime/src/runtime_module/chunk_name.rs b/crates/rspack_plugin_runtime/src/runtime_module/chunk_name.rs index ab16cd63681f..5512f8c47c57 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/chunk_name.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/chunk_name.rs @@ -37,7 +37,8 @@ impl RuntimeModule for ChunkNameRuntimeModule { if let Some(chunk_ukey) = self.chunk() { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); Ok(format!( "{} = {};", diff --git a/crates/rspack_plugin_runtime/src/runtime_module/chunk_prefetch_startup.rs b/crates/rspack_plugin_runtime/src/runtime_module/chunk_prefetch_startup.rs index 69ff75fabf42..fbdfbc4b3d98 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/chunk_prefetch_startup.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/chunk_prefetch_startup.rs @@ -57,7 +57,7 @@ impl RuntimeModule for ChunkPrefetchStartupRuntimeModule { if c.to_owned().eq(&chunk_ukey) { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph.chunks .expect_get(c) .id() } else { @@ -71,7 +71,7 @@ impl RuntimeModule for ChunkPrefetchStartupRuntimeModule { .filter_map(|c| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph.chunks .expect_get(c) .id() }) diff --git a/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_filename.rs b/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_filename.rs index 185af658a118..927780cdfee1 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_filename.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_filename.rs @@ -114,7 +114,8 @@ impl GetChunkFilenameRuntimeModule { let chunk_ukey = self.chunk().unwrap_or(self.chunk_ukey); compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(&chunk_ukey) .map(|chunk| { let runtime_requirements = get_chunk_runtime_requirements(compilation, &chunk.ukey()); @@ -134,7 +135,6 @@ impl GetChunkFilenameRuntimeModule { .chunk_graph .get_runtime_chunk_dependent_chunks_iterable( &chunk.ukey(), - &compilation.build_chunk_graph_artifact.chunk_by_ukey, &compilation.build_chunk_graph_artifact.chunk_group_by_ukey, ), ); @@ -231,7 +231,8 @@ impl RuntimeModule for GetChunkFilenameRuntimeModule { .filter_map(|chunk_ukey| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_ukey) }) .for_each(|chunk| { diff --git a/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_update_filename.rs b/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_update_filename.rs index f99075e7c032..98c9ac496d38 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_update_filename.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/get_chunk_update_filename.rs @@ -58,7 +58,8 @@ impl RuntimeModule for GetChunkUpdateFilenameRuntimeModule { if let Some(chunk_ukey) = self.chunk() { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); let filename = compilation .get_path( diff --git a/crates/rspack_plugin_runtime/src/runtime_module/get_main_filename.rs b/crates/rspack_plugin_runtime/src/runtime_module/get_main_filename.rs index 86efe12b975a..bfb08eaef5de 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/get_main_filename.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/get_main_filename.rs @@ -63,7 +63,8 @@ impl RuntimeModule for GetMainFilenameRuntimeModule { if let Some(chunk_ukey) = self.chunk() { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); let filename = compilation .get_path( diff --git a/crates/rspack_plugin_runtime/src/runtime_module/import_scripts_chunk_loading.rs b/crates/rspack_plugin_runtime/src/runtime_module/import_scripts_chunk_loading.rs index bc7b3948ed97..853b95ce988f 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/import_scripts_chunk_loading.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/import_scripts_chunk_loading.rs @@ -216,7 +216,8 @@ impl RuntimeModule for ImportScriptsChunkLoadingRuntimeModule { let runtime_template = context.runtime_template; let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&self.chunk().expect("The chunk should be attached.")); let runtime_requirements = get_chunk_runtime_requirements(compilation, &chunk.ukey()); diff --git a/crates/rspack_plugin_runtime/src/runtime_module/jsonp_chunk_loading.rs b/crates/rspack_plugin_runtime/src/runtime_module/jsonp_chunk_loading.rs index eddca941a77a..857b024b9628 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/jsonp_chunk_loading.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/jsonp_chunk_loading.rs @@ -282,7 +282,8 @@ impl RuntimeModule for JsonpChunkLoadingRuntimeModule { let runtime_template = context.runtime_template; let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&self.chunk().expect("The chunk should be attached")); let runtime_requirements = get_chunk_runtime_requirements(compilation, &chunk.ukey()); diff --git a/crates/rspack_plugin_runtime/src/runtime_module/module_chunk_loading.rs b/crates/rspack_plugin_runtime/src/runtime_module/module_chunk_loading.rs index 1d43a313d31a..0c5ec84abc99 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/module_chunk_loading.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/module_chunk_loading.rs @@ -272,7 +272,8 @@ impl RuntimeModule for ModuleChunkLoadingRuntimeModule { let runtime_template = context.runtime_template; let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&self.chunk().expect("The chunk should be attached.")); let runtime_requirements = get_chunk_runtime_requirements(compilation, &chunk.ukey()); diff --git a/crates/rspack_plugin_runtime/src/runtime_module/readfile_chunk_loading.rs b/crates/rspack_plugin_runtime/src/runtime_module/readfile_chunk_loading.rs index 72f921109bb2..d24019d1765c 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/readfile_chunk_loading.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/readfile_chunk_loading.rs @@ -253,7 +253,8 @@ impl RuntimeModule for ReadFileChunkLoadingRuntimeModule { let runtime_template = context.runtime_template; let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&self.chunk().expect("The chunk should be attached.")); let runtime_requirements = get_chunk_runtime_requirements(compilation, &chunk.ukey()); diff --git a/crates/rspack_plugin_runtime/src/runtime_module/require_js_chunk_loading.rs b/crates/rspack_plugin_runtime/src/runtime_module/require_js_chunk_loading.rs index 75d4dd8382df..bdcfb03c5e6f 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/require_js_chunk_loading.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/require_js_chunk_loading.rs @@ -264,7 +264,8 @@ impl RuntimeModule for RequireChunkLoadingRuntimeModule { let runtime_template = context.runtime_template; let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&self.chunk().expect("The chunk should be attached.")); let runtime_requirements = get_chunk_runtime_requirements(compilation, &chunk.ukey()); diff --git a/crates/rspack_plugin_runtime/src/runtime_module/runtime_id.rs b/crates/rspack_plugin_runtime/src/runtime_module/runtime_id.rs index 44959f070e0a..d7431a5146f3 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/runtime_id.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/runtime_id.rs @@ -38,7 +38,8 @@ impl RuntimeModule for RuntimeIdRuntimeModule { if let Some(chunk_ukey) = self.chunk() { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey); let runtime = chunk.runtime(); diff --git a/crates/rspack_plugin_runtime/src/runtime_module/startup_chunk_dependencies.rs b/crates/rspack_plugin_runtime/src/runtime_module/startup_chunk_dependencies.rs index fef0629dbe66..eccf960b121c 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/startup_chunk_dependencies.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/startup_chunk_dependencies.rs @@ -68,13 +68,13 @@ impl RuntimeModule for StartupChunkDependenciesRuntimeModule { .chunk_graph .get_chunk_entry_dependent_chunks_iterable( &chunk_ukey, - &compilation.build_chunk_graph_artifact.chunk_by_ukey, &compilation.build_chunk_graph_artifact.chunk_group_by_ukey, ) .map(|chunk_ukey| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&chunk_ukey) .expect_id() .clone() diff --git a/crates/rspack_plugin_runtime/src/runtime_module/utils.rs b/crates/rspack_plugin_runtime/src/runtime_module/utils.rs index 5cf82d01ebcc..f16eb9fc6fb2 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/utils.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/utils.rs @@ -20,7 +20,8 @@ pub fn get_initial_chunk_ids( match chunk { Some(chunk_ukey) => match compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(&chunk_ukey) { Some(chunk) => { @@ -31,7 +32,8 @@ pub fn get_initial_chunk_ids( .map(|chunk_ukey| { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); chunk.expect_id().clone() }) @@ -79,7 +81,8 @@ pub fn render_chunk_loading_hmr_state_expression( ) -> String { let chunk_loading = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_ukey) .and_then(|chunk| { chunk.get_entry_options(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) @@ -148,7 +151,8 @@ pub fn is_enabled_for_chunk( ) -> bool { let chunk_loading = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_ukey) .and_then(|chunk| { chunk.get_entry_options(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) diff --git a/crates/rspack_plugin_runtime/src/runtime_plugin.rs b/crates/rspack_plugin_runtime/src/runtime_plugin.rs index b96298e0450a..26f5bdfdfac2 100644 --- a/crates/rspack_plugin_runtime/src/runtime_plugin.rs +++ b/crates/rspack_plugin_runtime/src/runtime_plugin.rs @@ -183,7 +183,8 @@ async fn runtime_requirements_in_tree( { let c = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let has_async_chunks = c.has_async_chunks(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey); @@ -196,7 +197,8 @@ async fn runtime_requirements_in_tree( let library_type = { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); chunk .get_entry_options(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) @@ -225,7 +227,8 @@ async fn runtime_requirements_in_tree( RuntimeGlobals::PUBLIC_PATH => { let public_path = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey) .get_entry_options(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) .and_then(|options| options.public_path.clone()) diff --git a/crates/rspack_plugin_size_limits/src/lib.rs b/crates/rspack_plugin_size_limits/src/lib.rs index 1fa49ffc0d55..ec7d045a5233 100644 --- a/crates/rspack_plugin_size_limits/src/lib.rs +++ b/crates/rspack_plugin_size_limits/src/lib.rs @@ -45,7 +45,8 @@ impl SizeLimitsPlugin { async fn get_entrypoint_size(&self, entrypoint: &ChunkGroup, compilation: &Compilation) -> f64 { let mut size = 0.0; - for filename in entrypoint.get_files(&compilation.build_chunk_graph_artifact.chunk_by_ukey) { + for filename in entrypoint.get_files(&compilation.build_chunk_graph_artifact.chunk_graph.chunks) + { let asset = compilation.assets().get(&filename); if let Some(asset) = asset { @@ -184,7 +185,7 @@ async fn after_emit(&self, compilation: &mut Compilation) -> Result<()> { if is_over_size_limit { let mut files = vec![]; - for filename in entry.get_files(&compilation.build_chunk_graph_artifact.chunk_by_ukey) { + for filename in entry.get_files(&compilation.build_chunk_graph_artifact.chunk_graph.chunks) { let asset = compilation.assets().get(&filename); if let Some(asset) = asset @@ -222,7 +223,8 @@ async fn after_emit(&self, compilation: &mut Compilation) -> Result<()> { if !diagnostics.is_empty() { let has_async_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .values() .any(|chunk| { !chunk.can_be_initial(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) diff --git a/crates/rspack_plugin_split_chunks/src/common.rs b/crates/rspack_plugin_split_chunks/src/common.rs index 2079e95f61f4..00d21bb0e8a1 100644 --- a/crates/rspack_plugin_split_chunks/src/common.rs +++ b/crates/rspack_plugin_split_chunks/src/common.rs @@ -45,21 +45,24 @@ impl ChunkFilter { ChunkFilter::Regex(re) => { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); chunk.name().is_some_and(|name| re.test(name)) } ChunkFilter::Async => { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); !chunk.can_be_initial(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) } ChunkFilter::Initial => { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); chunk.can_be_initial(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) } diff --git a/crates/rspack_plugin_split_chunks/src/plugin/chunk.rs b/crates/rspack_plugin_split_chunks/src/plugin/chunk.rs index c28297b6e29c..63ccbc6880c9 100644 --- a/crates/rspack_plugin_split_chunks/src/plugin/chunk.rs +++ b/crates/rspack_plugin_split_chunks/src/plugin/chunk.rs @@ -54,7 +54,8 @@ impl SplitChunksPlugin { let candidates = module_group.chunks.iter().filter_map(|chunk| { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk); if compilation @@ -129,8 +130,8 @@ impl SplitChunksPlugin { module_group: &mut ModuleGroup, is_reuse_existing_chunk: &mut bool, is_reuse_existing_chunk_with_all_modules: &mut bool, - ) -> ChunkUkey { - if let Some(chunk_name) = &module_group.chunk_name { + ) -> rspack_error::Result { + Ok(if let Some(chunk_name) = &module_group.chunk_name { if let Some(chunk) = compilation .build_chunk_graph_artifact .named_chunks @@ -139,11 +140,9 @@ impl SplitChunksPlugin { *is_reuse_existing_chunk = true; *chunk } else { - let (new_chunk_ukey, created) = Compilation::add_named_chunk( - chunk_name.clone(), - &mut compilation.build_chunk_graph_artifact.chunk_by_ukey, - &mut compilation.build_chunk_graph_artifact.named_chunks, - ); + let (new_chunk_ukey, created) = compilation + .build_chunk_graph_artifact + .add_named_chunk(chunk_name.clone())?; if created && let Some(mut mutations) = compilation.incremental.mutations_write() { mutations.add(Mutation::ChunkAdd { chunk: new_chunk_ukey, @@ -151,18 +150,14 @@ impl SplitChunksPlugin { } let new_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&new_chunk_ukey); put_split_chunk_reason( new_chunk.chunk_reason_mut(), *is_reuse_existing_chunk_with_all_modules, ); - - compilation - .build_chunk_graph_artifact - .chunk_graph - .add_chunk(new_chunk.ukey()); new_chunk.ukey() } } else if module_group.cache_group_reuse_existing_chunk @@ -172,8 +167,10 @@ impl SplitChunksPlugin { *is_reuse_existing_chunk_with_all_modules = true; reusable_chunk } else { - let new_chunk_ukey = - Compilation::add_chunk(&mut compilation.build_chunk_graph_artifact.chunk_by_ukey); + let new_chunk_ukey = compilation + .build_chunk_graph_artifact + .chunk_graph + .create_chunk(None, rspack_core::ChunkKind::Normal)?; if let Some(mut mutations) = compilation.incremental.mutations_write() { mutations.add(Mutation::ChunkAdd { chunk: new_chunk_ukey, @@ -181,20 +178,16 @@ impl SplitChunksPlugin { } let new_chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&new_chunk_ukey); put_split_chunk_reason( new_chunk.chunk_reason_mut(), *is_reuse_existing_chunk_with_all_modules, ); - - compilation - .build_chunk_graph_artifact - .chunk_graph - .add_chunk(new_chunk.ukey()); new_chunk.ukey() - } + }) } /// Returns the selected source chunks for modules that can move into `new_chunk`. @@ -320,7 +313,7 @@ impl SplitChunksPlugin { } { - let chunk_by_ukey = &mut compilation.build_chunk_graph_artifact.chunk_by_ukey; + let chunk_by_ukey = &mut compilation.build_chunk_graph_artifact.chunk_graph.chunks; let chunk_group_by_ukey = &mut compilation.build_chunk_graph_artifact.chunk_group_by_ukey; let mut split_data = ChunkSplitData::default(); for original_chunk_ukey in original_chunks { diff --git a/crates/rspack_plugin_split_chunks/src/plugin/max_request.rs b/crates/rspack_plugin_split_chunks/src/plugin/max_request.rs index 9637eca3b831..739321d876c6 100644 --- a/crates/rspack_plugin_split_chunks/src/plugin/max_request.rs +++ b/crates/rspack_plugin_split_chunks/src/plugin/max_request.rs @@ -15,7 +15,7 @@ impl SplitChunksPlugin { cache_group: &CacheGroup, used_chunks: &mut Cow>, ) { - let chunk_db = &compilation.build_chunk_graph_artifact.chunk_by_ukey; + let chunk_db = &compilation.build_chunk_graph_artifact.chunk_graph.chunks; let chunk_group_db = &compilation.build_chunk_graph_artifact.chunk_group_by_ukey; let invalided_chunks = used_chunks .iter() diff --git a/crates/rspack_plugin_split_chunks/src/plugin/max_size.rs b/crates/rspack_plugin_split_chunks/src/plugin/max_size.rs index a7b31458c811..719d792db8f8 100644 --- a/crates/rspack_plugin_split_chunks/src/plugin/max_size.rs +++ b/crates/rspack_plugin_split_chunks/src/plugin/max_size.rs @@ -499,7 +499,7 @@ impl SplitChunksPlugin { let chunks_with_size_info_results = rspack_parallel::scope::<_, Result<_>>(|token| { compilation_ref .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph.chunks .values() .for_each(|chunk| { let s = unsafe { @@ -653,7 +653,8 @@ impl SplitChunksPlugin { }; let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&info.chunk); let delimiter = info.automatic_name_delimiter.as_str(); let mut name = chunk @@ -671,11 +672,9 @@ impl SplitChunksPlugin { if index != last_index { let old_chunk = chunk.ukey(); let new_chunk_ukey = if let Some(name) = name { - let (new_chunk_ukey, created) = Compilation::add_named_chunk( - name, - &mut compilation.build_chunk_graph_artifact.chunk_by_ukey, - &mut compilation.build_chunk_graph_artifact.named_chunks, - ); + let (new_chunk_ukey, created) = compilation + .build_chunk_graph_artifact + .add_named_chunk(name)?; if created && let Some(mut mutations) = compilation.incremental.mutations_write() { mutations.add(Mutation::ChunkAdd { chunk: new_chunk_ukey, @@ -683,8 +682,10 @@ impl SplitChunksPlugin { } new_chunk_ukey } else { - let new_chunk_ukey = - Compilation::add_chunk(&mut compilation.build_chunk_graph_artifact.chunk_by_ukey); + let new_chunk_ukey = compilation + .build_chunk_graph_artifact + .chunk_graph + .create_chunk(None, rspack_core::ChunkKind::Normal)?; if let Some(mut mutations) = compilation.incremental.mutations_write() { mutations.add(Mutation::ChunkAdd { chunk: new_chunk_ukey, @@ -693,21 +694,20 @@ impl SplitChunksPlugin { new_chunk_ukey }; - let [Some(new_part), Some(chunk)] = compilation - .build_chunk_graph_artifact - .chunk_by_ukey - .get_many_mut([&new_chunk_ukey, &old_chunk]) - else { - panic!("split_from_original_chunks failed") - }; - let new_part_ukey = new_part.ukey(); - chunk.split( - new_part, + let chunk_graph = &mut compilation.build_chunk_graph_artifact.chunk_graph; + let chunk = chunk_graph.chunks.expect_get(&old_chunk); + let reason = chunk.chunk_reason().map(ToString::to_string); + let filename_template = chunk.filename_template().cloned(); + chunk_graph.split_chunk( + &old_chunk, + &new_chunk_ukey, &mut compilation.build_chunk_graph_artifact.chunk_group_by_ukey, ); - *new_part.chunk_reason_mut() = chunk.chunk_reason().map(ToString::to_string); - if chunk.filename_template().is_some() { - new_part.set_filename_template(chunk.filename_template().cloned()); + let new_part = chunk_graph.chunks.expect_get_mut(&new_chunk_ukey); + let new_part_ukey = new_part.ukey(); + *new_part.chunk_reason_mut() = reason; + if filename_template.is_some() { + new_part.set_filename_template(filename_template); } if let Some(mut mutations) = compilation.incremental.mutations_write() { mutations.add(Mutation::ChunkSplit { @@ -717,11 +717,6 @@ impl SplitChunksPlugin { } for group_node in &group.nodes { - compilation - .build_chunk_graph_artifact - .chunk_graph - .add_chunk(new_part_ukey); - if let Some(module) = compilation.module_by_identifier(&group_node.module) && module_chunk_condition(module.as_ref(), &new_part_ukey, compilation) .await? diff --git a/crates/rspack_plugin_split_chunks/src/plugin/mod.rs b/crates/rspack_plugin_split_chunks/src/plugin/mod.rs index a728b52f7c51..ef51957d7074 100644 --- a/crates/rspack_plugin_split_chunks/src/plugin/mod.rs +++ b/crates/rspack_plugin_split_chunks/src/plugin/mod.rs @@ -10,7 +10,7 @@ use futures::future::BoxFuture; use itertools::Itertools; use rayon::iter::{IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator}; use rspack_collections::IdentifierMap; -use rspack_core::{ChunkUkey, Compilation, CompilationOptimizeChunks, Logger, Plugin}; +use rspack_core::{ChunkMap, ChunkUkey, Compilation, CompilationOptimizeChunks, Logger, Plugin}; use rspack_error::Result; use rspack_hook::{plugin, plugin_hook}; use rspack_util::{fx_hash::FxIndexMap, tracing_preset::TRACING_BENCH_TARGET}; @@ -105,10 +105,13 @@ impl SplitChunksPlugin { .collect::>(); logger.time_end(start); - let chunk_index_map: FxHashMap = { + let chunk_index_map = { + let chunks = &compilation.build_chunk_graph_artifact.chunk_graph.chunks; + let mut indices = ChunkMap::with_capacity(chunks.slot_count()); let mut ordered_chunks = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .values() .collect::>(); @@ -133,16 +136,14 @@ impl SplitChunksPlugin { (group.index, chunk_index) }); - ordered_chunks - .iter() - .enumerate() - .map(|(index, chunk)| { - ( - chunk.ukey(), - u32::try_from(index + 1).expect("chunk index should fit in u32"), - ) - }) - .collect() + for (index, chunk) in ordered_chunks.iter().enumerate() { + indices.insert( + chunks, + chunk.ukey(), + u32::try_from(index + 1).expect("chunk index should fit in u32"), + ); + } + indices }; let start = logger.time("prepare cache groups"); @@ -209,7 +210,7 @@ impl SplitChunksPlugin { combinator.prepare_group_by_used_exports( &all_modules, &compilation.exports_info_artifact, - &compilation.build_chunk_graph_artifact.chunk_by_ukey, + &compilation.build_chunk_graph_artifact.chunk_graph.chunks, available_module_chunks, &chunk_index_map, ); @@ -250,13 +251,14 @@ impl SplitChunksPlugin { &mut module_group, &mut is_reuse_existing_chunk, &mut is_reuse_existing_chunk_with_all_modules, - ); + )?; tracing::trace!( "{module_group_key}, get Chunk {:?} with is_reuse_existing_chunk: {is_reuse_existing_chunk:?} and {is_reuse_existing_chunk_with_all_modules:?}", compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(&new_chunk) .chunk_reason() ); @@ -429,7 +431,8 @@ impl SplitChunksPlugin { // a chunk it did not actually use. let new_chunk_mut = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get_mut(&new_chunk); if let Some(chunk_reason) = new_chunk_mut.chunk_reason_mut() { chunk_reason.push_str(&format!(" (cache group: {})", cache_group.key.as_str())); diff --git a/crates/rspack_plugin_split_chunks/src/plugin/module_group.rs b/crates/rspack_plugin_split_chunks/src/plugin/module_group.rs index 5aa7349d5de5..55411801026e 100644 --- a/crates/rspack_plugin_split_chunks/src/plugin/module_group.rs +++ b/crates/rspack_plugin_split_chunks/src/plugin/module_group.rs @@ -12,8 +12,8 @@ use futures::{ use itertools::Either; use rayon::prelude::*; use rspack_core::{ - ChunkByUkey, ChunkUkey, Compilation, ExportsInfoArtifact, Module, ModuleIdentifier, - RuntimeKeyMap, UsageKey, get_runtime_key, + Chunk, ChunkMap, ChunkSlotMap, ChunkUkey, Compilation, ExportsInfoArtifact, Module, + ModuleIdentifier, RuntimeKeyMap, UsageKey, get_runtime_key, }; use rspack_error::{Result, ToStringResultToRspackResultExt}; use rspack_util::{fx_hash::FxDashMap, tracing_preset::TRACING_BENCH_TARGET}; @@ -49,11 +49,7 @@ struct ChunkCombinationData { } impl ChunkCombination { - fn new( - key: ChunksKey, - chunks: FxHashSet, - _indices: &FxHashMap, - ) -> Self { + fn new(key: ChunksKey, chunks: FxHashSet, _indices: &ChunkMap) -> Self { Self { key, data: Arc::new(ChunkCombinationData { chunks }), @@ -180,10 +176,7 @@ async fn process_name_requests( Ok(()) } -fn get_key>( - chunks: I, - chunk_index_map: &FxHashMap, -) -> ChunksKey { +fn get_key>(chunks: I, chunk_index_map: &ChunkMap) -> ChunksKey { let mut sorted_chunk_ukeys = chunks .map(|chunk| { // Increment each chunk index by 1 to avoid hashing the value 0 with FxHasher, which would always return a hash of 0 @@ -272,8 +265,8 @@ impl Combinator { module_identifier: &ModuleIdentifier, module_chunks: &FxHashSet, exports_info_artifact: &ExportsInfoArtifact, - chunk_by_ukey: &ChunkByUkey, - chunk_index_map: &FxHashMap, + chunk_by_ukey: &ChunkSlotMap, + chunk_index_map: &ChunkMap, ) -> Vec { // A single chunk cannot produce runtime-dependent usage groups. if module_chunks.len() == 1 { @@ -416,7 +409,7 @@ impl Combinator { &mut self, all_modules: &[ModuleIdentifier], module_chunks: &ModuleChunks, - chunk_index_map: &FxHashMap, + chunk_index_map: &ChunkMap, min_chunks: usize, ) { self.non_used_exports_chunks_keys = all_modules @@ -466,9 +459,9 @@ impl Combinator { &mut self, all_modules: &[ModuleIdentifier], exports_info_artifact: &ExportsInfoArtifact, - chunk_by_ukey: &ChunkByUkey, + chunk_by_ukey: &ChunkSlotMap, module_chunks: &ModuleChunks, - chunk_index_map: &FxHashMap, + chunk_index_map: &ChunkMap, ) { let (grouped_by_exports, used_exports_chunks): (Vec<_>, Vec<_>) = all_modules .par_iter() @@ -549,7 +542,7 @@ impl SplitChunksPlugin { cache_groups: Vec>, compilation: &Compilation, module_chunks: &ModuleChunks, - chunk_index_map: &FxHashMap, + chunk_index_map: &ChunkMap, ) -> Result { let module_graph = compilation.get_module_graph(); let module_group_map: FxDashMap = FxDashMap::default(); @@ -951,7 +944,7 @@ fn merge_matched_item_into_module_group_map( matched_item: MatchedItem<'_>, chunk_name: Option, module_group_map: &FxDashMap, - chunk_index_map: &FxHashMap, + chunk_index_map: &ChunkMap, ) { let MatchedItem { module, diff --git a/crates/rspack_plugin_sri/src/asset.rs b/crates/rspack_plugin_sri/src/asset.rs index 97a80ba0a5bc..b54132604695 100644 --- a/crates/rspack_plugin_sri/src/asset.rs +++ b/crates/rspack_plugin_sri/src/asset.rs @@ -54,7 +54,13 @@ See https://w3c.github.io/webappsec-subresource-integrity/#cross-origin-data-lea for batch in batches { let chunks = batch .into_iter() - .filter_map(|c| compilation.build_chunk_graph_artifact.chunk_by_ukey.get(&c)) + .filter_map(|c| { + compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .get(&c) + }) .collect::>(); let results = chunks @@ -223,7 +229,8 @@ fn digest_chunks(compilation: &Compilation) -> Vec> { } let Some(chunk) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk_ukey) else { continue; @@ -311,7 +318,8 @@ pub async fn detect_unresolved_integrity( let mut contain_unresolved_files = vec![]; for chunk in compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .values() { for file in chunk.files() { diff --git a/crates/rspack_plugin_sri/src/runtime.rs b/crates/rspack_plugin_sri/src/runtime.rs index 108673693d94..fdc7508d81bf 100644 --- a/crates/rspack_plugin_sri/src/runtime.rs +++ b/crates/rspack_plugin_sri/src/runtime.rs @@ -52,11 +52,13 @@ impl RuntimeModule for SRIHashVariableRuntimeModule { async fn generate(&self, context: &RuntimeModuleGenerateContext<'_>) -> Result { let compilation = context.compilation; - let Some(chunk) = self - .chunk() - .as_ref() - .and_then(|c| compilation.build_chunk_graph_artifact.chunk_by_ukey.get(c)) - else { + let Some(chunk) = self.chunk().as_ref().and_then(|c| { + compilation + .build_chunk_graph_artifact + .chunk_graph + .chunks + .get(c) + }) else { return Err(error!( "Generate sri runtime module failed: chunk not found" )); @@ -68,7 +70,8 @@ impl RuntimeModule for SRIHashVariableRuntimeModule { .filter_map(|c| { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(c)?; let id = chunk.id()?; let rendered_hash = chunk.rendered_hash( @@ -130,7 +133,8 @@ impl RuntimeModule for SRIHashVariableRuntimeModule { .map(|c| { compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(c) .expect_id() }) diff --git a/crates/rspack_plugin_sri/src/util.rs b/crates/rspack_plugin_sri/src/util.rs index 9280f0711398..1c62295ac05d 100644 --- a/crates/rspack_plugin_sri/src/util.rs +++ b/crates/rspack_plugin_sri/src/util.rs @@ -74,7 +74,8 @@ fn recurse_chunk( if let Some(chunk) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(chunk) { for group in chunk.groups() { diff --git a/crates/rspack_plugin_wasm/src/loading_plugin.rs b/crates/rspack_plugin_wasm/src/loading_plugin.rs index 05d0658071f2..8474829c066c 100644 --- a/crates/rspack_plugin_wasm/src/loading_plugin.rs +++ b/crates/rspack_plugin_wasm/src/loading_plugin.rs @@ -182,7 +182,8 @@ async fn universal_compile_async_wasm_plugin_runtime_requirements_in_tree( let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let wasm_loading = chunk .get_entry_options(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey) diff --git a/crates/rspack_plugin_wasm/src/wasm_plugin.rs b/crates/rspack_plugin_wasm/src/wasm_plugin.rs index ae3dae9dca4c..9ed9e2fc8cff 100644 --- a/crates/rspack_plugin_wasm/src/wasm_plugin.rs +++ b/crates/rspack_plugin_wasm/src/wasm_plugin.rs @@ -41,7 +41,8 @@ async fn render_manifest( ) -> Result<()> { let chunk = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .expect_get(chunk_ukey); let module_graph = &compilation.get_module_graph(); diff --git a/website/docs/en/api/javascript-api/architecture.mdx b/website/docs/en/api/javascript-api/architecture.mdx index 9ad13bfdfe04..4762f60582e3 100644 --- a/website/docs/en/api/javascript-api/architecture.mdx +++ b/website/docs/en/api/javascript-api/architecture.mdx @@ -81,6 +81,8 @@ Follow these rules: - If information is needed later, save JavaScript-owned strings or numbers such as identifiers, names, paths, and hashes, or convert the data into an ordinary JavaScript object. - After a watch rebuild, obtain objects again from the new `Compilation` instead of reusing objects saved from the previous build. +A `Chunk` handle belongs to the `Compilation` that supplied it. Passing a Chunk from another compiler or an earlier build to `compilation.chunks.has()` returns `false`; passing it to a ChunkGraph method throws an error. Internal reuse during an incremental build does not extend the lifetime of the earlier JavaScript handle. + ## Hook and callback execution The Rust compiler can use multiple threads to run several independent compilation tasks at the same time. JavaScript hooks, loaders, and other callbacks cannot run directly on those Rust threads. They must be scheduled back into the JavaScript environment in which the Rspack compiler was created. diff --git a/website/docs/zh/api/javascript-api/architecture.mdx b/website/docs/zh/api/javascript-api/architecture.mdx index 4d3151df5ee4..f8ecedf9974b 100644 --- a/website/docs/zh/api/javascript-api/architecture.mdx +++ b/website/docs/zh/api/javascript-api/architecture.mdx @@ -81,6 +81,8 @@ watch 模式下,应使用当前构建 hook 提供的 `Compilation` 和 native- - 如果稍后仍需要某些信息,应提前保存 identifier、name、path、hash 等字符串或数字,或者转换成普通 JavaScript 对象。 - watch rebuild 后,应从新的 `Compilation` 重新获取对象,不要复用上一次构建保存的对象。 +`Chunk` 句柄属于提供它的 `Compilation`。将其他 compiler 或之前构建的 Chunk 传给 `compilation.chunks.has()` 会返回 `false`;将其传给 ChunkGraph 方法会抛出错误。增量构建内部复用 Chunk 数据,不会延长之前 JavaScript 句柄的有效期。 + ## Hook 和回调执行 Rust compiler 可以使用多个线程,同时执行多个彼此独立的编译任务。但是,JavaScript hook、loader 和其他回调不能直接在这些 Rust 线程上运行,它们必须被调度回创建 Rspack compiler 的 JavaScript 环境。 diff --git a/xtask/benchmark/benches/groups/build_chunk_graph.rs b/xtask/benchmark/benches/groups/build_chunk_graph.rs index edd5754eaba7..3ae1f3a71417 100644 --- a/xtask/benchmark/benches/groups/build_chunk_graph.rs +++ b/xtask/benchmark/benches/groups/build_chunk_graph.rs @@ -213,7 +213,8 @@ pub fn build_chunk_graph_benchmark_inner(c: &mut Criterion) { compiler .compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .len(), NUM_MODULES / 10 ); @@ -353,7 +354,6 @@ fn configure_swc_loader(builder: &mut CompilerBuilder) { } fn reset_chunk_graph_state(compilation: &mut Compilation) { - compilation.build_chunk_graph_artifact.chunk_by_ukey = Default::default(); compilation.build_chunk_graph_artifact.chunk_graph = Default::default(); compilation.build_chunk_graph_artifact.chunk_group_by_ukey = Default::default(); compilation.build_chunk_graph_artifact.entrypoints = Default::default(); diff --git a/xtask/benchmark/benches/groups/compilation_stages.rs b/xtask/benchmark/benches/groups/compilation_stages.rs index f2e1b8dbad47..bf2295e2671b 100644 --- a/xtask/benchmark/benches/groups/compilation_stages.rs +++ b/xtask/benchmark/benches/groups/compilation_stages.rs @@ -14,8 +14,8 @@ use rspack::builder::Builder as _; use rspack_benchmark::Criterion; use rspack_collections::IdentifierSet; use rspack_core::{ - AssignRuntimeIdsPass, AsyncModulesArtifact, CacheOptions, ChunkByUkey, ChunkContentHash, - ChunkGraph, ChunkNamedIdArtifact, ChunkUkey, CodeGenerationJob, CodeGenerationPass, Compilation, + AssignRuntimeIdsPass, AsyncModulesArtifact, CacheOptions, ChunkContentHash, ChunkGraph, + ChunkNamedIdArtifact, ChunkUkey, CodeGenerationJob, CodeGenerationPass, Compilation, CompilationAsset, CompilationAssets, Compiler, CreateChunkAssetsPass, CreateHashPass, CreateModuleAssetsPass, CreateModuleHashesPass, DEFAULT_DELIMITER, LogType, MangleExportsOption, Mode, ModuleCodeGenerationContext, ModuleIdsArtifact, Optimization, OptimizeCodeGenerationPass, @@ -245,11 +245,6 @@ pub(crate) fn split_chunks_benchmark(c: &mut Criterion, rt: &Runtime) { .build_chunk_graph_artifact .chunk_graph .clone(); - let initial_chunk_by_ukey = compiler - .compilation - .build_chunk_graph_artifact - .chunk_by_ukey - .clone(); let initial_chunk_group_by_ukey = compiler .compilation .build_chunk_graph_artifact @@ -275,11 +270,10 @@ pub(crate) fn split_chunks_benchmark(c: &mut Criterion, rt: &Runtime) { .build_chunk_graph_artifact .named_chunks .clone(); - let chunk_count_before = initial_chunk_by_ukey.len(); + let chunk_count_before = initial_chunk_graph.chunks.len(); let restore_initial_chunk_state = |compilation: &mut Compilation| { compilation.build_chunk_graph_artifact.chunk_graph = initial_chunk_graph.clone(); - compilation.build_chunk_graph_artifact.chunk_by_ukey = initial_chunk_by_ukey.clone(); compilation.build_chunk_graph_artifact.chunk_group_by_ukey = initial_chunk_group_by_ukey.clone(); compilation.build_chunk_graph_artifact.entrypoints = initial_entrypoints.clone(); @@ -297,7 +291,8 @@ pub(crate) fn split_chunks_benchmark(c: &mut Criterion, rt: &Runtime) { let chunk_count_after = compiler .compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .len(); assert!( chunk_count_after > chunk_count_before, @@ -324,7 +319,8 @@ pub(crate) fn split_chunks_benchmark(c: &mut Criterion, rt: &Runtime) { compiler .compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .len(), ); }, @@ -347,33 +343,35 @@ pub(crate) fn create_chunk_ids_benchmark(c: &mut Criterion, rt: &Runtime) { }); assert_no_compilation_errors(&compiler.compilation, "create_chunk_ids setup"); - let initial_chunk_by_ukey = compiler + let initial_chunk_graph = compiler .compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph .clone(); let compiler = RefCell::new(compiler); c.bench_function("rust@create_chunk_ids", |b| { b.iter_batched( || { - ( - initial_chunk_by_ukey.clone(), - ChunkNamedIdArtifact::default(), - ) + let mut compiler = compiler.borrow_mut(); + compiler + .compilation + .build_chunk_graph_artifact + .chunk_graph + .clone_from(&initial_chunk_graph); + compiler.compilation.named_chunk_ids_artifact = ChunkNamedIdArtifact::default().into(); }, - |(mut chunk_by_ukey, mut named_chunk_ids_artifact)| { - let compiler = compiler.borrow(); - rt.block_on(async { - run_chunk_ids_hook( - &compiler.compilation, - &mut chunk_by_ukey, - &mut named_chunk_ids_artifact, - ) - .await + |_| { + let mut compiler = compiler.borrow_mut(); + rt.block_on(run_chunk_ids_hook(&mut compiler.compilation)) .unwrap(); - }); - black_box(named_chunk_ids_artifact.chunk_ids.len()); + black_box( + compiler + .compilation + .named_chunk_ids_artifact + .chunk_ids + .len(), + ); }, BatchSize::PerIteration, ); @@ -394,33 +392,35 @@ pub(crate) fn create_named_chunk_ids_benchmark(c: &mut Criterion, rt: &Runtime) }); assert_no_compilation_errors(&compiler.compilation, "create_named_chunk_ids setup"); - let initial_chunk_by_ukey = compiler + let initial_chunk_graph = compiler .compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph .clone(); let compiler = RefCell::new(compiler); c.bench_function("rust@create_named_chunk_ids", |b| { b.iter_batched( || { - ( - initial_chunk_by_ukey.clone(), - ChunkNamedIdArtifact::default(), - ) + let mut compiler = compiler.borrow_mut(); + compiler + .compilation + .build_chunk_graph_artifact + .chunk_graph + .clone_from(&initial_chunk_graph); + compiler.compilation.named_chunk_ids_artifact = ChunkNamedIdArtifact::default().into(); }, - |(mut chunk_by_ukey, mut named_chunk_ids_artifact)| { - let compiler = compiler.borrow(); - rt.block_on(async { - run_chunk_ids_hook( - &compiler.compilation, - &mut chunk_by_ukey, - &mut named_chunk_ids_artifact, - ) - .await + |_| { + let mut compiler = compiler.borrow_mut(); + rt.block_on(run_chunk_ids_hook(&mut compiler.compilation)) .unwrap(); - }); - black_box(named_chunk_ids_artifact.chunk_ids.len()); + black_box( + compiler + .compilation + .named_chunk_ids_artifact + .chunk_ids + .len(), + ); }, BatchSize::PerIteration, ); @@ -891,11 +891,6 @@ fn register_create_concatenate_module_benchmark( .build_chunk_graph_artifact .chunk_graph .clone(); - let initial_chunk_by_ukey = compiler - .compilation - .build_chunk_graph_artifact - .chunk_by_ukey - .clone(); rt.block_on(async { run_optimize_chunk_modules_hook(&mut compiler.compilation) @@ -922,10 +917,6 @@ fn register_create_concatenate_module_benchmark( .get_module_graph_mut() .reset(); compiler.compilation.build_chunk_graph_artifact.chunk_graph = initial_chunk_graph.clone(); - compiler - .compilation - .build_chunk_graph_artifact - .chunk_by_ukey = initial_chunk_by_ukey.clone(); clear_concatenation_statistics_logs(&compiler.compilation); let compiler = RefCell::new(compiler); @@ -949,10 +940,6 @@ fn register_create_concatenate_module_benchmark( .get_module_graph_mut() .checkpoint(); compiler.compilation.build_chunk_graph_artifact.chunk_graph = initial_chunk_graph.clone(); - compiler - .compilation - .build_chunk_graph_artifact - .chunk_by_ukey = initial_chunk_by_ukey.clone(); clear_concatenation_statistics_logs(&compiler.compilation); }, |_| { @@ -1500,43 +1487,20 @@ async fn run_module_ids_hook(compilation: &mut Compilation) -> Result<()> { Ok(()) } -async fn run_chunk_ids_hook( - compilation: &Compilation, - chunk_by_ukey: &mut ChunkByUkey, - named_chunk_ids_artifact: &mut ChunkNamedIdArtifact, -) -> Result<()> { - let mut diagnostics = vec![]; +async fn run_chunk_ids_hook(compilation: &mut Compilation) -> Result<()> { compilation .plugin_driver .clone() .compilation_hooks .chunk_ids - .call( - compilation, - chunk_by_ukey, - named_chunk_ids_artifact, - &mut diagnostics, - ) + .call(compilation) .await?; - assert!( - diagnostics.is_empty(), - "chunk_ids benchmark setup should not produce diagnostics" - ); + assert_no_compilation_errors(compilation, "chunk_ids benchmark"); Ok(()) } async fn run_chunk_ids_on_compilation(compilation: &mut Compilation) -> Result<()> { - let mut chunk_by_ukey = std::mem::take(&mut compilation.build_chunk_graph_artifact.chunk_by_ukey); - let mut named_chunk_ids_artifact = compilation.named_chunk_ids_artifact.steal(); - run_chunk_ids_hook( - compilation, - &mut chunk_by_ukey, - &mut named_chunk_ids_artifact, - ) - .await?; - compilation.build_chunk_graph_artifact.chunk_by_ukey = chunk_by_ukey; - compilation.named_chunk_ids_artifact = named_chunk_ids_artifact.into(); - Ok(()) + run_chunk_ids_hook(compilation).await } async fn run_create_module_hashes_pass(compiler: &mut Compiler) -> Result<()> { @@ -1550,13 +1514,13 @@ async fn run_code_generation_pass(compiler: &mut Compiler) -> Result<()> { #[derive(Clone)] struct ChunkAssetStateSnapshot { assets: CompilationAssets, - chunk_by_ukey: ChunkByUkey, + chunk_graph: ChunkGraph, } fn snapshot_chunk_asset_state(compilation: &Compilation) -> ChunkAssetStateSnapshot { ChunkAssetStateSnapshot { assets: compilation.assets().clone(), - chunk_by_ukey: compilation.build_chunk_graph_artifact.chunk_by_ukey.clone(), + chunk_graph: compilation.build_chunk_graph_artifact.chunk_graph.clone(), } } @@ -1568,7 +1532,10 @@ fn restore_chunk_asset_state(compilation: &mut Compilation, snapshot: &ChunkAsse for (asset_name, asset) in snapshot.assets.clone() { compilation.emit_asset(asset_name, asset); } - compilation.build_chunk_graph_artifact.chunk_by_ukey = snapshot.chunk_by_ukey.clone(); + compilation + .build_chunk_graph_artifact + .chunk_graph + .clone_from(&snapshot.chunk_graph); } fn chunk_asset_totals(compilation: &Compilation) -> (usize, usize, usize) { @@ -1578,7 +1545,8 @@ fn chunk_asset_totals(compilation: &Compilation) -> (usize, usize, usize) { for chunk in compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .values() { emitted_files += chunk.files().len(); @@ -1692,10 +1660,7 @@ async fn compute_module_hashes(compilation: &Compilation) -> Result { for runtime in compilation .build_chunk_graph_artifact .chunk_graph - .get_module_runtimes_iter( - module_identifier, - &compilation.build_chunk_graph_artifact.chunk_by_ukey, - ) + .get_module_runtimes_iter(module_identifier) { let hash = module.get_runtime_hash(compilation, Some(runtime)).await?; black_box(hash); @@ -1709,7 +1674,8 @@ async fn compute_module_hashes(compilation: &Compilation) -> Result { async fn compute_chunk_hashes(compilation: &Compilation) -> Result { let chunk_ukeys = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .keys() .copied() .collect::>(); @@ -1735,7 +1701,8 @@ async fn process_chunk_hash( let mut hasher = RspackHasher::from(&compilation.options.output); if let Some(chunk) = compilation .build_chunk_graph_artifact - .chunk_by_ukey + .chunk_graph + .chunks .get(&chunk_ukey) { chunk.update_hash(&mut hasher, compilation); @@ -1778,13 +1745,12 @@ async fn compute_concatenated_module_codegen( concatenated_modules: &[rspack_core::ModuleIdentifier], ) -> Result { let chunk_graph = &compilation.build_chunk_graph_artifact.chunk_graph; - let chunk_by_ukey = &compilation.build_chunk_graph_artifact.chunk_by_ukey; let module_graph = compilation.get_module_graph(); let mut jobs = Vec::new(); for &module_identifier in concatenated_modules { let mut grouped_jobs = HashMap::::new(); - for runtime in chunk_graph.get_module_runtimes_iter(module_identifier, chunk_by_ukey) { + for runtime in chunk_graph.get_module_runtimes_iter(module_identifier) { let hash = ChunkGraph::get_module_hash(compilation, module_identifier, runtime) .expect("concatenated module should have a module hash") .clone(); From 8385bb9e3189c129a60c3a1ef5a87bbd3feb64ce Mon Sep 17 00:00:00 2001 From: Cong-Cong Pan Date: Thu, 17 Sep 2026 01:29:40 +0800 Subject: [PATCH 2/2] fix: address chunk storage migration CI checks --- .../src/chunk_graph/chunk_graph_chunk.rs | 60 +++++++++---------- .../build_chunk_graph/code_splitter.rs | 2 +- crates/rspack_core_test/Cargo.toml | 6 +- .../src/lib.rs | 5 +- 4 files changed, 38 insertions(+), 35 deletions(-) diff --git a/crates/rspack_core/src/chunk_graph/chunk_graph_chunk.rs b/crates/rspack_core/src/chunk_graph/chunk_graph_chunk.rs index d6f4f6bf038d..9f590affe7f7 100644 --- a/crates/rspack_core/src/chunk_graph/chunk_graph_chunk.rs +++ b/crates/rspack_core/src/chunk_graph/chunk_graph_chunk.rs @@ -836,36 +836,6 @@ impl super::ChunkGraphTopology { } } -#[cfg(test)] -mod tests { - use super::ChunkId; - - #[test] - fn chunk_id_serialize_matches_runtime_numeric_rules() { - assert_eq!(simd_json::to_string(&ChunkId::from("903")).unwrap(), "903"); - assert_eq!( - simd_json::to_string(&ChunkId::from("01")).unwrap(), - "\"01\"" - ); - assert_eq!( - simd_json::to_string(&ChunkId::from("main")).unwrap(), - "\"main\"" - ); - assert_eq!( - simd_json::to_string(&ChunkId::from("4294967295")).unwrap(), - "4294967295" - ); - assert_eq!( - simd_json::to_string(&ChunkId::from("4294967296")).unwrap(), - "\"4294967296\"" - ); - assert_eq!( - simd_json::to_string(&vec![ChunkId::from("01"), ChunkId::from("903")]).unwrap(), - "[\"01\",903]" - ); - } -} - impl ChunkGraph { pub fn set_chunk_runtime_requirements( compilation: &mut Compilation, @@ -1127,3 +1097,33 @@ impl ChunkGraph { }) } } + +#[cfg(test)] +mod tests { + use super::ChunkId; + + #[test] + fn chunk_id_serialize_matches_runtime_numeric_rules() { + assert_eq!(simd_json::to_string(&ChunkId::from("903")).unwrap(), "903"); + assert_eq!( + simd_json::to_string(&ChunkId::from("01")).unwrap(), + "\"01\"" + ); + assert_eq!( + simd_json::to_string(&ChunkId::from("main")).unwrap(), + "\"main\"" + ); + assert_eq!( + simd_json::to_string(&ChunkId::from("4294967295")).unwrap(), + "4294967295" + ); + assert_eq!( + simd_json::to_string(&ChunkId::from("4294967296")).unwrap(), + "\"4294967296\"" + ); + assert_eq!( + simd_json::to_string(&vec![ChunkId::from("01"), ChunkId::from("903")]).unwrap(), + "[\"01\",903]" + ); + } +} diff --git a/crates/rspack_core/src/compilation/build_chunk_graph/code_splitter.rs b/crates/rspack_core/src/compilation/build_chunk_graph/code_splitter.rs index 35d2a3995e32..426797361274 100644 --- a/crates/rspack_core/src/compilation/build_chunk_graph/code_splitter.rs +++ b/crates/rspack_core/src/compilation/build_chunk_graph/code_splitter.rs @@ -929,7 +929,7 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on .build_chunk_graph_artifact .chunk_graph .chunks - .expect_get_mut(&ukey) + .expect_get_mut(ukey) } None => { let (chunk_ukey, created) = compilation diff --git a/crates/rspack_core_test/Cargo.toml b/crates/rspack_core_test/Cargo.toml index d8fbe38e7b19..246028d28d64 100644 --- a/crates/rspack_core_test/Cargo.toml +++ b/crates/rspack_core_test/Cargo.toml @@ -5,7 +5,11 @@ name = "rspack_core_test" publish = false version.workspace = true -[dependencies] +[lib] +doctest = false +test = false + +[dev-dependencies] rspack_cacheable = { workspace = true } rspack_collections = { workspace = true } rspack_core = { workspace = true } diff --git a/crates/rspack_plugin_remove_empty_chunks/src/lib.rs b/crates/rspack_plugin_remove_empty_chunks/src/lib.rs index 77a60e05e935..50e85e4d4733 100644 --- a/crates/rspack_plugin_remove_empty_chunks/src/lib.rs +++ b/crates/rspack_plugin_remove_empty_chunks/src/lib.rs @@ -35,10 +35,9 @@ impl RemoveEmptyChunksPlugin { .build_chunk_graph_artifact .remove_chunk(chunk_ukey) .is_some() + && let Some(mut mutations) = compilation.incremental.mutations_write() { - if let Some(mut mutations) = compilation.incremental.mutations_write() { - mutations.add(Mutation::ChunkRemove { chunk: *chunk_ukey }); - } + mutations.add(Mutation::ChunkRemove { chunk: *chunk_ukey }); } }