1use std::{
2 collections::{VecDeque, hash_map},
3 fmt::Debug,
4 hash::{BuildHasherDefault, Hash},
5 sync::{
6 Arc,
7 atomic::{AtomicBool, AtomicU32, Ordering},
8 },
9};
10
11use dashmap::DashSet;
12use futures::future::BoxFuture;
13use indexmap::IndexMap;
14use itertools::Itertools;
15use rayon::prelude::*;
16use rspack_cacheable::{
17 cacheable,
18 with::{AsOption, AsPreset},
19};
20use rspack_collections::{
21 DatabaseItem, IdentifierDashMap, IdentifierMap, IdentifierSet, UkeyMap, UkeySet,
22};
23use rspack_error::{Diagnostic, Result, ToStringResultToRspackResultExt};
24use rspack_fs::{IntermediateFileSystem, ReadableFileSystem, WritableFileSystem};
25use rspack_hash::{RspackHash, RspackHashDigest};
26use rspack_hook::define_hook;
27use rspack_paths::{ArcPath, ArcPathIndexSet, ArcPathSet};
28use rspack_sources::BoxSource;
29use rspack_tasks::CompilerContext;
30#[cfg(allocative)]
31use rspack_util::allocative;
32use rspack_util::{itoa, tracing_preset::TRACING_BENCH_TARGET};
33use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet, FxHasher};
34use tracing::instrument;
35
36use super::{CompilerId, rebuild::CompilationRecords};
37use crate::{
38 AsyncModulesArtifact, BindingCell, BoxDependency, BoxModule, CacheCount, CacheOptions,
39 CgcRuntimeRequirementsArtifact, CgmHashArtifact, CgmRuntimeRequirementsArtifact, Chunk,
40 ChunkByUkey, ChunkContentHash, ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey, ChunkHashesArtifact,
41 ChunkIdsArtifact, ChunkKind, ChunkRenderArtifact, ChunkRenderCacheArtifact, ChunkRenderResult,
42 ChunkUkey, CodeGenerationJob, CodeGenerationResult, CodeGenerationResults, CompilationLogger,
43 CompilationLogging, CompilerOptions, ConcatenationScope, DependenciesDiagnosticsArtifact,
44 DependencyCodeGeneration, DependencyTemplate, DependencyTemplateType, DependencyType, Entry,
45 EntryData, EntryOptions, EntryRuntime, Entrypoint, ExecuteModuleId, Filename, ImportPhase,
46 ImportVarMap, ImportedByDeferModulesArtifact, Logger, MemoryGCStorage, ModuleFactory,
47 ModuleGraph, ModuleGraphCacheArtifact, ModuleGraphPartial, ModuleIdentifier, ModuleIdsArtifact,
48 ModuleStaticCacheArtifact, PathData, ResolverFactory, RuntimeGlobals, RuntimeKeyMap, RuntimeMode,
49 RuntimeModule, RuntimeSpec, RuntimeSpecMap, RuntimeTemplate, SharedPluginDriver,
50 SideEffectsOptimizeArtifact, SourceType, Stats, ValueCacheVersions,
51 build_chunk_graph::{build_chunk_graph, build_chunk_graph_new},
52 compilation::make::{
53 MakeArtifact, ModuleExecutor, UpdateParam, finish_make, make, update_module_graph,
54 },
55 get_runtime_key,
56 incremental::{self, Incremental, IncrementalPasses, Mutation},
57 is_source_equal,
58 old_cache::{Cache as OldCache, CodeSplittingCache, use_code_splitting_cache},
59 to_identifier,
60};
61
62define_hook!(CompilationAddEntry: Series(compilation: &mut Compilation, entry_name: Option<&str>));
63define_hook!(CompilationBuildModule: Series(compiler_id: CompilerId, compilation_id: CompilationId, module: &mut BoxModule),tracing=false);
64define_hook!(CompilationRevokedModules: Series(compilation: &Compilation, revoked_modules: &IdentifierSet));
65define_hook!(CompilationStillValidModule: Series(compiler_id: CompilerId, compilation_id: CompilationId, module: &mut BoxModule));
66define_hook!(CompilationSucceedModule: Series(compiler_id: CompilerId, compilation_id: CompilationId, module: &mut BoxModule),tracing=false);
67define_hook!(CompilationExecuteModule:
68 Series(module: &ModuleIdentifier, runtime_modules: &IdentifierSet, code_generation_results: &BindingCell<CodeGenerationResults>, execute_module_id: &ExecuteModuleId));
69define_hook!(CompilationFinishModules: Series(compilation: &mut Compilation));
70define_hook!(CompilationSeal: Series(compilation: &mut Compilation));
71define_hook!(CompilationConcatenationScope: SeriesBail(compilation: &Compilation, curr_module: ModuleIdentifier) -> ConcatenationScope);
72define_hook!(CompilationOptimizeDependencies: SeriesBail(compilation: &mut Compilation) -> bool);
73define_hook!(CompilationOptimizeModules: SeriesBail(compilation: &mut Compilation) -> bool);
74define_hook!(CompilationAfterOptimizeModules: Series(compilation: &mut Compilation));
75define_hook!(CompilationOptimizeChunks: SeriesBail(compilation: &mut Compilation) -> bool);
76define_hook!(CompilationOptimizeTree: Series(compilation: &mut Compilation));
77define_hook!(CompilationOptimizeChunkModules: SeriesBail(compilation: &mut Compilation) -> bool);
78define_hook!(CompilationModuleIds: Series(compilation: &mut Compilation));
79define_hook!(CompilationChunkIds: Series(compilation: &mut Compilation));
80define_hook!(CompilationRuntimeModule: Series(compilation: &mut Compilation, module: &ModuleIdentifier, chunk: &ChunkUkey));
81define_hook!(CompilationAdditionalModuleRuntimeRequirements: Series(compilation: &Compilation, module_identifier: &ModuleIdentifier, runtime_requirements: &mut RuntimeGlobals),tracing=false);
82define_hook!(CompilationRuntimeRequirementInModule: SeriesBail(compilation: &Compilation, module_identifier: &ModuleIdentifier, all_runtime_requirements: &RuntimeGlobals, runtime_requirements: &RuntimeGlobals, runtime_requirements_mut: &mut RuntimeGlobals),tracing=false);
83define_hook!(CompilationAdditionalChunkRuntimeRequirements: Series(compilation: &mut Compilation, chunk_ukey: &ChunkUkey, runtime_requirements: &mut RuntimeGlobals));
84define_hook!(CompilationRuntimeRequirementInChunk: SeriesBail(compilation: &mut Compilation, chunk_ukey: &ChunkUkey, all_runtime_requirements: &RuntimeGlobals, runtime_requirements: &RuntimeGlobals, runtime_requirements_mut: &mut RuntimeGlobals));
85define_hook!(CompilationAdditionalTreeRuntimeRequirements: Series(compilation: &mut Compilation, chunk_ukey: &ChunkUkey, runtime_requirements: &mut RuntimeGlobals));
86define_hook!(CompilationRuntimeRequirementInTree: SeriesBail(compilation: &mut Compilation, chunk_ukey: &ChunkUkey, all_runtime_requirements: &RuntimeGlobals, runtime_requirements: &RuntimeGlobals, runtime_requirements_mut: &mut RuntimeGlobals));
87define_hook!(CompilationOptimizeCodeGeneration: Series(compilation: &mut Compilation));
88define_hook!(CompilationAfterCodeGeneration: Series(compilation: &mut Compilation));
89define_hook!(CompilationChunkHash: Series(compilation: &Compilation, chunk_ukey: &ChunkUkey, hasher: &mut RspackHash),tracing=false);
90define_hook!(CompilationContentHash: Series(compilation: &Compilation, chunk_ukey: &ChunkUkey, hashes: &mut HashMap<SourceType, RspackHash>));
91define_hook!(CompilationDependentFullHash: SeriesBail(compilation: &Compilation, chunk_ukey: &ChunkUkey) -> bool);
92define_hook!(CompilationRenderManifest: Series(compilation: &Compilation, chunk_ukey: &ChunkUkey, manifest: &mut Vec<RenderManifestEntry>, diagnostics: &mut Vec<Diagnostic>),tracing=false);
93define_hook!(CompilationChunkAsset: Series(compilation: &Compilation, chunk_ukey: &ChunkUkey, filename: &str));
94define_hook!(CompilationProcessAssets: Series(compilation: &mut Compilation));
95define_hook!(CompilationAfterProcessAssets: Series(compilation: &mut Compilation));
96define_hook!(CompilationAfterSeal: Series(compilation: &mut Compilation),tracing=true);
97
98#[derive(Debug, Default)]
99pub struct CompilationHooks {
100 pub add_entry: CompilationAddEntryHook,
101 pub build_module: CompilationBuildModuleHook,
102 pub revoked_modules: CompilationRevokedModulesHook,
103 pub concatenation_scope: CompilationConcatenationScopeHook,
104 pub still_valid_module: CompilationStillValidModuleHook,
105 pub succeed_module: CompilationSucceedModuleHook,
106 pub execute_module: CompilationExecuteModuleHook,
107 pub finish_modules: CompilationFinishModulesHook,
108 pub seal: CompilationSealHook,
109 pub optimize_dependencies: CompilationOptimizeDependenciesHook,
110 pub optimize_modules: CompilationOptimizeModulesHook,
111 pub after_optimize_modules: CompilationAfterOptimizeModulesHook,
112 pub optimize_chunks: CompilationOptimizeChunksHook,
113 pub optimize_tree: CompilationOptimizeTreeHook,
114 pub optimize_chunk_modules: CompilationOptimizeChunkModulesHook,
115 pub module_ids: CompilationModuleIdsHook,
116 pub chunk_ids: CompilationChunkIdsHook,
117 pub runtime_module: CompilationRuntimeModuleHook,
118 pub additional_module_runtime_requirements: CompilationAdditionalModuleRuntimeRequirementsHook,
119 pub runtime_requirement_in_module: CompilationRuntimeRequirementInModuleHook,
120 pub additional_chunk_runtime_requirements: CompilationAdditionalChunkRuntimeRequirementsHook,
121 pub runtime_requirement_in_chunk: CompilationRuntimeRequirementInChunkHook,
122 pub additional_tree_runtime_requirements: CompilationAdditionalTreeRuntimeRequirementsHook,
123 pub runtime_requirement_in_tree: CompilationRuntimeRequirementInTreeHook,
124 pub optimize_code_generation: CompilationOptimizeCodeGenerationHook,
125 pub after_code_generation: CompilationAfterCodeGenerationHook,
126 pub chunk_hash: CompilationChunkHashHook,
127 pub content_hash: CompilationContentHashHook,
128 pub dependent_full_hash: CompilationDependentFullHashHook,
129 pub render_manifest: CompilationRenderManifestHook,
130 pub chunk_asset: CompilationChunkAssetHook,
131 pub process_assets: CompilationProcessAssetsHook,
132 pub after_process_assets: CompilationAfterProcessAssetsHook,
133 pub after_seal: CompilationAfterSealHook,
134}
135
136#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
137#[cfg_attr(allocative, derive(allocative::Allocative))]
138pub struct CompilationId(pub u32);
139
140impl CompilationId {
141 pub fn new() -> Self {
142 Self(COMPILATION_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed))
143 }
144}
145
146impl Default for CompilationId {
147 fn default() -> Self {
148 Self::new()
149 }
150}
151
152static COMPILATION_ID: AtomicU32 = AtomicU32::new(0);
153
154macro_rules! process_runtime_requirement_hook_macro {
158 ($name: ident, $s: ty, $c: ty) => {
159 async fn $name(
160 self: $s,
161 requirements: &mut RuntimeGlobals,
162 call_hook: impl for<'a> Fn(
163 $c,
164 &'a RuntimeGlobals,
165 &'a RuntimeGlobals,
166 &'a mut RuntimeGlobals,
167 ) -> BoxFuture<'a, Result<()>>,
168 ) -> Result<()> {
169 let mut runtime_requirements_mut = *requirements;
170 let mut runtime_requirements;
171
172 loop {
173 runtime_requirements = runtime_requirements_mut;
174 runtime_requirements_mut = RuntimeGlobals::default();
175 call_hook(
179 self,
180 requirements,
181 &runtime_requirements,
182 &mut runtime_requirements_mut,
183 )
184 .await?;
185
186 runtime_requirements_mut =
188 runtime_requirements_mut.difference(requirements.intersection(runtime_requirements_mut));
189 if runtime_requirements_mut.is_empty() {
190 break;
191 } else {
192 requirements.insert(runtime_requirements_mut);
193 }
194 }
195 Ok(())
196 }
197 };
198}
199
200#[derive(Debug)]
201pub struct Compilation {
202 id: CompilationId,
204 compiler_id: CompilerId,
205 pub hot_index: u32,
211 pub records: Option<CompilationRecords>,
212 pub options: Arc<CompilerOptions>,
213 pub entries: Entry,
214 pub global_entry: EntryData,
215 other_module_graph: Option<ModuleGraphPartial>,
216 pub dependency_factories: HashMap<DependencyType, Arc<dyn ModuleFactory>>,
217 pub dependency_templates: HashMap<DependencyTemplateType, Arc<dyn DependencyTemplate>>,
218 pub runtime_modules: IdentifierMap<Box<dyn RuntimeModule>>,
219 pub runtime_modules_hash: IdentifierMap<RspackHashDigest>,
220 pub runtime_modules_code_generation_source: IdentifierMap<BoxSource>,
221 pub chunk_graph: ChunkGraph,
222 pub chunk_by_ukey: ChunkByUkey,
223 pub chunk_group_by_ukey: ChunkGroupByUkey,
224 pub entrypoints: IndexMap<String, ChunkGroupUkey>,
225 pub async_entrypoints: Vec<ChunkGroupUkey>,
226 assets: CompilationAssets,
227 assets_related_in: HashMap<String, HashSet<String>>,
228 pub emitted_assets: DashSet<String, BuildHasherDefault<FxHasher>>,
229 diagnostics: Vec<Diagnostic>,
230 logging: CompilationLogging,
231 pub plugin_driver: SharedPluginDriver,
232 pub buildtime_plugin_driver: SharedPluginDriver,
233 pub resolver_factory: Arc<ResolverFactory>,
234 pub loader_resolver_factory: Arc<ResolverFactory>,
235 pub named_chunks: HashMap<String, ChunkUkey>,
236 pub named_chunk_groups: HashMap<String, ChunkGroupUkey>,
237 pub runtime_template: RuntimeTemplate,
238
239 pub async_modules_artifact: AsyncModulesArtifact,
241 pub dependencies_diagnostics_artifact: DependenciesDiagnosticsArtifact,
243 pub side_effects_optimize_artifact: SideEffectsOptimizeArtifact,
245 pub module_ids_artifact: ModuleIdsArtifact,
247 pub chunk_ids_artifact: ChunkIdsArtifact,
249 pub code_generation_results: BindingCell<CodeGenerationResults>,
251 pub cgm_hash_artifact: CgmHashArtifact,
253 pub cgm_runtime_requirements_artifact: CgmRuntimeRequirementsArtifact,
255 pub cgc_runtime_requirements_artifact: CgcRuntimeRequirementsArtifact,
257 pub chunk_hashes_artifact: ChunkHashesArtifact,
259 pub chunk_render_artifact: ChunkRenderArtifact,
261 pub module_graph_cache_artifact: ModuleGraphCacheArtifact,
263 pub module_static_cache_artifact: ModuleStaticCacheArtifact,
265 pub chunk_render_cache_artifact: ChunkRenderCacheArtifact,
267 pub imported_by_defer_modules_artifact: ImportedByDeferModulesArtifact,
268
269 pub code_generated_modules: IdentifierSet,
270 pub build_time_executed_modules: IdentifierSet,
271 pub old_cache: Arc<OldCache>,
272 pub code_splitting_cache: CodeSplittingCache,
273 pub incremental: Incremental,
274
275 pub hash: Option<RspackHashDigest>,
276
277 pub file_dependencies: ArcPathIndexSet,
278 pub context_dependencies: ArcPathIndexSet,
279 pub missing_dependencies: ArcPathIndexSet,
280 pub build_dependencies: ArcPathIndexSet,
281
282 pub value_cache_versions: ValueCacheVersions,
283
284 import_var_map: IdentifierDashMap<RuntimeKeyMap<ImportVarMap>>,
285
286 pub module_executor: Option<ModuleExecutor>,
288 in_finish_make: AtomicBool,
289
290 pub modified_files: ArcPathSet,
291 pub removed_files: ArcPathSet,
292 pub make_artifact: MakeArtifact,
293 pub input_filesystem: Arc<dyn ReadableFileSystem>,
294
295 pub intermediate_filesystem: Arc<dyn IntermediateFileSystem>,
296 pub output_filesystem: Arc<dyn WritableFileSystem>,
297
298 pub is_rebuild: bool,
302 pub compiler_context: Arc<CompilerContext>,
303}
304
305impl Compilation {
306 pub const OPTIMIZE_CHUNKS_STAGE_BASIC: i32 = -10;
307 pub const OPTIMIZE_CHUNKS_STAGE_DEFAULT: i32 = 0;
308 pub const OPTIMIZE_CHUNKS_STAGE_ADVANCED: i32 = 10;
309
310 pub const PROCESS_ASSETS_STAGE_ADDITIONAL: i32 = -2000;
311 pub const PROCESS_ASSETS_STAGE_PRE_PROCESS: i32 = -1000;
312 pub const PROCESS_ASSETS_STAGE_DERIVED: i32 = -200;
313 pub const PROCESS_ASSETS_STAGE_ADDITIONS: i32 = -100;
314 pub const PROCESS_ASSETS_STAGE_OPTIMIZE: i32 = 100;
315 pub const PROCESS_ASSETS_STAGE_OPTIMIZE_COUNT: i32 = 200;
316 pub const PROCESS_ASSETS_STAGE_OPTIMIZE_COMPATIBILITY: i32 = 300;
317 pub const PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE: i32 = 400;
318 pub const PROCESS_ASSETS_STAGE_DEV_TOOLING: i32 = 500;
319 pub const PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE: i32 = 700;
320 pub const PROCESS_ASSETS_STAGE_SUMMARIZE: i32 = 1000;
321 pub const PROCESS_ASSETS_STAGE_OPTIMIZE_HASH: i32 = 2500;
322 pub const PROCESS_ASSETS_STAGE_AFTER_OPTIMIZE_HASH: i32 = 2600;
323 pub const PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER: i32 = 3000;
324 pub const PROCESS_ASSETS_STAGE_ANALYSE: i32 = 4000;
325 pub const PROCESS_ASSETS_STAGE_REPORT: i32 = 5000;
326
327 #[allow(clippy::too_many_arguments)]
328 pub fn new(
329 compiler_id: CompilerId,
330 options: Arc<CompilerOptions>,
331 plugin_driver: SharedPluginDriver,
332 buildtime_plugin_driver: SharedPluginDriver,
333 resolver_factory: Arc<ResolverFactory>,
334 loader_resolver_factory: Arc<ResolverFactory>,
335 records: Option<CompilationRecords>,
336 old_cache: Arc<OldCache>,
337 incremental: Incremental,
338 module_executor: Option<ModuleExecutor>,
339 modified_files: ArcPathSet,
340 removed_files: ArcPathSet,
341 input_filesystem: Arc<dyn ReadableFileSystem>,
342 intermediate_filesystem: Arc<dyn IntermediateFileSystem>,
343 output_filesystem: Arc<dyn WritableFileSystem>,
344 is_rebuild: bool,
345 compiler_context: Arc<CompilerContext>,
346 ) -> Self {
347 Self {
348 id: CompilationId::new(),
349 compiler_id,
350 hot_index: 0,
351 runtime_template: RuntimeTemplate::new(options.output.environment),
352 records,
353 options: options.clone(),
354 other_module_graph: None,
355 dependency_factories: Default::default(),
356 dependency_templates: Default::default(),
357 runtime_modules: Default::default(),
358 runtime_modules_hash: Default::default(),
359 runtime_modules_code_generation_source: Default::default(),
360 chunk_by_ukey: Default::default(),
361 chunk_group_by_ukey: Default::default(),
362 entries: Default::default(),
363 global_entry: Default::default(),
364 chunk_graph: Default::default(),
365 entrypoints: Default::default(),
366 async_entrypoints: Default::default(),
367 assets: Default::default(),
368 assets_related_in: Default::default(),
369 emitted_assets: Default::default(),
370 diagnostics: Default::default(),
371 logging: Default::default(),
372 plugin_driver,
373 buildtime_plugin_driver,
374 resolver_factory,
375 loader_resolver_factory,
376 named_chunks: Default::default(),
377 named_chunk_groups: Default::default(),
378
379 async_modules_artifact: Default::default(),
380 imported_by_defer_modules_artifact: Default::default(),
381 dependencies_diagnostics_artifact: Default::default(),
382 side_effects_optimize_artifact: Default::default(),
383 module_ids_artifact: Default::default(),
384 chunk_ids_artifact: Default::default(),
385 code_generation_results: Default::default(),
386 cgm_hash_artifact: Default::default(),
387 cgm_runtime_requirements_artifact: Default::default(),
388 cgc_runtime_requirements_artifact: Default::default(),
389 chunk_hashes_artifact: Default::default(),
390 chunk_render_artifact: Default::default(),
391 module_graph_cache_artifact: Default::default(),
392 module_static_cache_artifact: Default::default(),
393 code_generated_modules: Default::default(),
394 chunk_render_cache_artifact: ChunkRenderCacheArtifact::new(MemoryGCStorage::new(
395 match &options.cache {
396 CacheOptions::Memory { max_generations } => max_generations.unwrap_or(1),
397 CacheOptions::Disabled => 0, },
399 )),
400 build_time_executed_modules: Default::default(),
401 old_cache,
402 incremental,
403 code_splitting_cache: Default::default(),
404
405 hash: None,
406
407 file_dependencies: Default::default(),
408 context_dependencies: Default::default(),
409 missing_dependencies: Default::default(),
410 build_dependencies: Default::default(),
411
412 value_cache_versions: ValueCacheVersions::default(),
413
414 import_var_map: IdentifierDashMap::default(),
415
416 module_executor,
417 in_finish_make: AtomicBool::new(false),
418
419 make_artifact: Default::default(),
420 modified_files,
421 removed_files,
422 input_filesystem,
423
424 intermediate_filesystem,
425 output_filesystem,
426 is_rebuild,
427 compiler_context,
428 }
429 }
430
431 pub fn id(&self) -> CompilationId {
432 self.id
433 }
434
435 pub fn compiler_id(&self) -> CompilerId {
436 self.compiler_id
437 }
438
439 pub fn swap_make_artifact_with_compilation(&mut self, other: &mut Compilation) {
440 std::mem::swap(&mut self.make_artifact, &mut other.make_artifact);
441 }
442 pub fn swap_make_artifact(&mut self, make_artifact: &mut MakeArtifact) {
443 std::mem::swap(&mut self.make_artifact, make_artifact);
444 }
445
446 pub fn get_module_graph(&self) -> ModuleGraph<'_> {
447 if let Some(other_module_graph) = &self.other_module_graph {
448 ModuleGraph::new(
449 [
450 Some(self.make_artifact.get_module_graph_partial()),
451 Some(other_module_graph),
452 ],
453 None,
454 )
455 } else {
456 ModuleGraph::new(
457 [Some(self.make_artifact.get_module_graph_partial()), None],
458 None,
459 )
460 }
461 }
462
463 pub fn module_by_identifier(&self, identifier: &ModuleIdentifier) -> Option<&BoxModule> {
465 if let Some(other_module_graph) = &self.other_module_graph
466 && let Some(module) = other_module_graph.modules.get(identifier)
467 {
468 return module.as_ref();
469 };
470
471 if let Some(module) = self
472 .make_artifact
473 .get_module_graph_partial()
474 .modules
475 .get(identifier)
476 {
477 return module.as_ref();
478 }
479
480 None
481 }
482
483 pub fn get_module_graph_mut(&mut self) -> ModuleGraph<'_> {
484 if let Some(other) = &mut self.other_module_graph {
485 ModuleGraph::new(
486 [Some(self.make_artifact.get_module_graph_partial()), None],
487 Some(other),
488 )
489 } else {
490 ModuleGraph::new(
491 [None, None],
492 Some(self.make_artifact.get_module_graph_partial_mut()),
493 )
494 }
495 }
496
497 pub fn file_dependencies(
498 &self,
499 ) -> (
500 impl Iterator<Item = &ArcPath>,
501 impl Iterator<Item = &ArcPath>,
502 impl Iterator<Item = &ArcPath>,
503 ) {
504 let all_files = self
505 .make_artifact
506 .file_dependencies
507 .files()
508 .chain(&self.file_dependencies);
509 let added_files = self
510 .make_artifact
511 .file_dependencies
512 .added_files()
513 .chain(&self.file_dependencies);
514 let removed_files = self.make_artifact.file_dependencies.removed_files();
515 (all_files, added_files, removed_files)
516 }
517
518 pub fn context_dependencies(
519 &self,
520 ) -> (
521 impl Iterator<Item = &ArcPath>,
522 impl Iterator<Item = &ArcPath>,
523 impl Iterator<Item = &ArcPath>,
524 ) {
525 let all_files = self
526 .make_artifact
527 .context_dependencies
528 .files()
529 .chain(&self.context_dependencies);
530 let added_files = self
531 .make_artifact
532 .context_dependencies
533 .added_files()
534 .chain(&self.file_dependencies);
535 let removed_files = self.make_artifact.context_dependencies.removed_files();
536 (all_files, added_files, removed_files)
537 }
538
539 pub fn missing_dependencies(
540 &self,
541 ) -> (
542 impl Iterator<Item = &ArcPath>,
543 impl Iterator<Item = &ArcPath>,
544 impl Iterator<Item = &ArcPath>,
545 ) {
546 let all_files = self
547 .make_artifact
548 .missing_dependencies
549 .files()
550 .chain(&self.missing_dependencies);
551 let added_files = self
552 .make_artifact
553 .missing_dependencies
554 .added_files()
555 .chain(&self.file_dependencies);
556 let removed_files = self.make_artifact.missing_dependencies.removed_files();
557 (all_files, added_files, removed_files)
558 }
559
560 pub fn build_dependencies(
561 &self,
562 ) -> (
563 impl Iterator<Item = &ArcPath>,
564 impl Iterator<Item = &ArcPath>,
565 impl Iterator<Item = &ArcPath>,
566 ) {
567 let all_files = self
568 .make_artifact
569 .build_dependencies
570 .files()
571 .chain(&self.build_dependencies);
572 let added_files = self
573 .make_artifact
574 .build_dependencies
575 .added_files()
576 .chain(&self.file_dependencies);
577 let removed_files = self.make_artifact.build_dependencies.removed_files();
578 (all_files, added_files, removed_files)
579 }
580
581 pub fn get_import_var(
583 &self,
584 module: ModuleIdentifier,
585 target_module: Option<&BoxModule>,
586 user_request: &str,
587 phase: ImportPhase,
588 runtime: Option<&RuntimeSpec>,
589 ) -> String {
590 let mut runtime_map = self.import_var_map.entry(module).or_default();
591 let import_var_map_of_module = runtime_map
592 .entry(
593 runtime
594 .map(|r| get_runtime_key(r).to_string())
595 .unwrap_or_default(),
596 )
597 .or_default();
598 let len = import_var_map_of_module.len();
599 let is_deferred = phase.is_defer()
600 && !target_module
601 .map(|m| m.build_meta().has_top_level_await)
602 .unwrap_or_default();
603
604 match import_var_map_of_module.entry((target_module.map(|m| m.identifier()), is_deferred)) {
605 hash_map::Entry::Occupied(occ) => occ.get().clone(),
606 hash_map::Entry::Vacant(vac) => {
607 let mut b = itoa::Buffer::new();
608 let import_var = format!(
609 "{}__WEBPACK_{}IMPORTED_MODULE_{}__",
610 to_identifier(user_request),
611 match phase {
612 ImportPhase::Evaluation => "",
613 ImportPhase::Source => "",
614 ImportPhase::Defer => "DEFERRED_",
615 },
616 b.format(len)
617 );
618 vac.insert(import_var.clone());
619 import_var
620 }
621 }
622 }
623
624 pub async fn add_entry(&mut self, entry: BoxDependency, options: EntryOptions) -> Result<()> {
625 let entry_id = *entry.id();
626 let entry_name = options.name.clone();
627 self.get_module_graph_mut().add_dependency(entry);
628 if let Some(name) = &entry_name {
629 if let Some(data) = self.entries.get_mut(name) {
630 data.dependencies.push(entry_id);
631 data.options.merge(options)?;
632 } else {
633 let data = EntryData {
634 dependencies: vec![entry_id],
635 include_dependencies: vec![],
636 options,
637 };
638 self.entries.insert(name.to_owned(), data);
639 }
640 } else {
641 self.global_entry.dependencies.push(entry_id);
642 }
643
644 self
645 .plugin_driver
646 .clone()
647 .compilation_hooks
648 .add_entry
649 .call(self, entry_name.as_deref())
650 .await?;
651
652 Ok(())
653 }
654
655 pub async fn add_entry_batch(&mut self, args: Vec<(BoxDependency, EntryOptions)>) -> Result<()> {
656 for (entry, options) in args {
657 self.add_entry(entry, options).await?;
658 }
659
660 let make_artifact = std::mem::take(&mut self.make_artifact);
661 self.make_artifact = update_module_graph(
662 self,
663 make_artifact,
664 vec![UpdateParam::BuildEntry(
665 self
666 .entries
667 .values()
668 .flat_map(|item| item.all_dependencies())
669 .chain(self.global_entry.all_dependencies())
670 .copied()
671 .collect(),
672 )],
673 )
674 .await?;
675
676 Ok(())
677 }
678
679 pub async fn add_include(&mut self, args: Vec<(BoxDependency, EntryOptions)>) -> Result<()> {
680 if !self.in_finish_make.load(Ordering::Acquire) {
681 return Err(rspack_error::Error::error(
682 "You can only call `add_include` during the finish make stage".into(),
683 ));
684 }
685
686 for (entry, options) in args {
687 let entry_id = *entry.id();
688 self.get_module_graph_mut().add_dependency(entry);
689 if let Some(name) = options.name.clone() {
690 if let Some(data) = self.entries.get_mut(&name) {
691 data.include_dependencies.push(entry_id);
692 } else {
693 let data = EntryData {
694 dependencies: vec![],
695 include_dependencies: vec![entry_id],
696 options,
697 };
698 self.entries.insert(name, data);
699 }
700 } else {
701 self.global_entry.include_dependencies.push(entry_id);
702 }
703 }
704
705 let make_artifact = std::mem::take(&mut self.make_artifact);
708 self.make_artifact = update_module_graph(
709 self,
710 make_artifact,
711 vec![UpdateParam::BuildEntry(
712 self
713 .entries
714 .values()
715 .flat_map(|item| item.all_dependencies())
716 .chain(self.global_entry.all_dependencies())
717 .copied()
718 .collect(),
719 )],
720 )
721 .await?;
722
723 Ok(())
724 }
725
726 fn set_asset_info(
727 &mut self,
728 name: &str,
729 new_info: Option<&AssetInfo>,
730 old_info: Option<&AssetInfo>,
731 ) {
732 if let Some(old_info) = old_info
733 && let Some(source_map) = &old_info.related.source_map
734 && let Some(entry) = self.assets_related_in.get_mut(source_map)
735 {
736 entry.remove(name);
737 }
738 if let Some(new_info) = new_info
739 && let Some(source_map) = new_info.related.source_map.clone()
740 {
741 let entry = self.assets_related_in.entry(source_map).or_default();
742 entry.insert(name.to_string());
743 }
744 }
745
746 pub fn update_asset(
747 &mut self,
748 filename: &str,
749 updater: impl FnOnce(
750 BoxSource,
751 BindingCell<AssetInfo>,
752 ) -> Result<(BoxSource, BindingCell<AssetInfo>)>,
753 ) -> Result<()> {
754 let assets = &mut self.assets;
755
756 let (old_info, new_source, new_info) = match assets.remove(filename) {
757 Some(CompilationAsset {
758 source: Some(source),
759 info: old_info,
760 }) => {
761 let (new_source, new_info) = updater(source, old_info.clone())?;
762 (old_info, new_source, new_info)
763 }
764 _ => {
765 return Err(rspack_error::error!(
766 "Called Compilation.updateAsset for not existing filename {}",
767 filename
768 ));
769 }
770 };
771 self.set_asset_info(filename, Some(&new_info), Some(&old_info));
772 self.assets.insert(
773 filename.to_owned(),
774 CompilationAsset {
775 source: Some(new_source),
776 info: new_info,
777 },
778 );
779 Ok(())
780 }
781 #[instrument("Compilation:emit_asset",skip_all, fields(filename = filename))]
782 pub fn emit_asset(&mut self, filename: String, asset: CompilationAsset) {
783 if let Some(mut original) = self.assets.remove(&filename)
784 && let Some(original_source) = &original.source
785 && let Some(asset_source) = asset.get_source()
786 {
787 let is_source_equal = is_source_equal(original_source, asset_source);
788 if !is_source_equal {
789 tracing::error!(
790 "Emit Duplicate Filename({}), is_source_equal: {:?}",
791 filename,
792 is_source_equal
793 );
794 self.push_diagnostic(
795 rspack_error::error!(
796 "Conflict: Multiple assets emit different content to the same filename {}{}",
797 filename,
798 ""
800 )
801 .into(),
802 );
803 self.set_asset_info(&filename, Some(asset.get_info()), None);
804 self.assets.insert(filename, asset);
805 return;
806 }
807 self.set_asset_info(&filename, Some(asset.get_info()), Some(original.get_info()));
808 original.info = asset.info;
809 self.assets.insert(filename, original);
810 } else {
811 self.set_asset_info(&filename, Some(asset.get_info()), None);
812 self.assets.insert(filename, asset);
813 }
814 }
815
816 pub fn delete_asset(&mut self, filename: &str) {
817 if let Some(asset) = self.assets.remove(filename) {
818 self.set_asset_info(filename, None, Some(asset.get_info()));
819
820 if let Some(source_map) = &asset.info.related.source_map {
821 self.delete_asset(source_map);
822 }
823 self.chunk_by_ukey.iter_mut().for_each(|(_, chunk)| {
824 chunk.remove_file(filename);
825 chunk.remove_auxiliary_file(filename);
826 });
827 }
828 }
829
830 pub fn rename_asset(&mut self, filename: &str, new_name: String) {
831 if let Some(asset) = self.assets.remove(filename) {
832 if let Some(related_in_info) = self.assets_related_in.get(filename) {
834 for name in related_in_info {
835 if let Some(asset) = self.assets.get_mut(name) {
836 asset.get_info_mut().related.source_map = Some(new_name.to_string());
837 }
838 }
839 }
840 self.set_asset_info(filename, None, Some(asset.get_info()));
841 self.set_asset_info(&new_name, Some(asset.get_info()), None);
842
843 self.assets.insert(new_name.clone(), asset);
844
845 self.chunk_by_ukey.iter_mut().for_each(|(_, chunk)| {
846 if chunk.remove_file(filename) {
847 chunk.add_file(new_name.clone());
848 }
849
850 if chunk.remove_auxiliary_file(filename) {
851 chunk.add_auxiliary_file(new_name.clone());
852 }
853 });
854 }
855 }
856
857 pub fn par_rename_assets(&mut self, renames: Vec<(String, String)>) {
862 self
863 .chunk_by_ukey
864 .values_mut()
865 .par_bridge()
866 .for_each(|chunk| {
867 for (old_name, new_name) in renames.iter() {
868 if chunk.remove_file(old_name) {
869 chunk.add_file(new_name.clone());
870 }
871
872 if chunk.remove_auxiliary_file(old_name) {
873 chunk.add_auxiliary_file(new_name.clone());
874 }
875 }
876 });
877
878 for (old_name, new_name) in renames {
879 if let Some(asset) = self.assets.remove(&old_name) {
880 if let Some(related_in_info) = self.assets_related_in.get(&old_name) {
882 for related_in_name in related_in_info {
883 if let Some(asset) = self.assets.get_mut(related_in_name) {
884 asset.get_info_mut().related.source_map = Some(new_name.clone());
885 }
886 }
887 }
888 self.set_asset_info(&old_name, None, Some(asset.get_info()));
889 self.set_asset_info(&new_name, Some(asset.get_info()), None);
890
891 self.assets.insert(new_name, asset);
892 }
893 }
894 }
895
896 pub fn assets(&self) -> &CompilationAssets {
897 &self.assets
898 }
899
900 pub fn assets_mut(&mut self) -> &mut CompilationAssets {
901 &mut self.assets
902 }
903
904 pub fn entrypoints(&self) -> &IndexMap<String, ChunkGroupUkey> {
905 &self.entrypoints
906 }
907
908 pub fn push_diagnostic(&mut self, diagnostic: Diagnostic) {
909 self.diagnostics.push(diagnostic);
910 }
911
912 pub fn extend_diagnostics(&mut self, diagnostics: impl IntoIterator<Item = Diagnostic>) {
913 self.diagnostics.extend(diagnostics);
914 }
915
916 pub fn diagnostics(&self) -> &[Diagnostic] {
917 &self.diagnostics
918 }
919
920 pub fn diagnostics_mut(&mut self) -> &mut Vec<Diagnostic> {
921 &mut self.diagnostics
922 }
923
924 pub fn get_errors(&self) -> impl Iterator<Item = &Diagnostic> {
925 self.diagnostics.iter().filter(|d| d.is_error())
926 }
927
928 pub fn get_errors_sorted(&self) -> impl Iterator<Item = &Diagnostic> {
935 let get_offset = |d: &Diagnostic| {
936 d.labels
937 .as_ref()
938 .and_then(|l| l.first())
939 .map(|l| l.offset)
940 .unwrap_or_default()
941 };
942 self
943 .get_errors()
944 .sorted_by(|a, b| match a.module_identifier.cmp(&b.module_identifier) {
945 std::cmp::Ordering::Equal => get_offset(a).cmp(&get_offset(b)),
946 other => other,
947 })
948 }
949
950 pub fn get_warnings(&self) -> impl Iterator<Item = &Diagnostic> {
951 self.diagnostics.iter().filter(|d| d.is_warn())
952 }
953
954 pub fn get_warnings_sorted(&self) -> impl Iterator<Item = &Diagnostic> {
961 let get_offset = |d: &Diagnostic| {
962 d.labels
963 .as_ref()
964 .and_then(|l| l.first())
965 .map(|l| l.offset)
966 .unwrap_or_default()
967 };
968 self
969 .get_warnings()
970 .sorted_by(|a, b| match a.module_identifier.cmp(&b.module_identifier) {
971 std::cmp::Ordering::Equal => get_offset(a).cmp(&get_offset(b)),
972 other => other,
973 })
974 }
975
976 pub fn get_logging(&self) -> &CompilationLogging {
977 &self.logging
978 }
979
980 pub fn get_stats(&self) -> Stats<'_> {
981 Stats::new(self)
982 }
983
984 pub fn add_named_chunk(
985 name: String,
986 chunk_by_ukey: &mut ChunkByUkey,
987 named_chunks: &mut HashMap<String, ChunkUkey>,
988 ) -> (ChunkUkey, bool) {
989 let existed_chunk_ukey = named_chunks.get(&name);
990 if let Some(chunk_ukey) = existed_chunk_ukey {
991 assert!(chunk_by_ukey.contains(chunk_ukey));
992 (*chunk_ukey, false)
993 } else {
994 let chunk = Chunk::new(Some(name.clone()), ChunkKind::Normal);
995 let ukey = chunk.ukey();
996 named_chunks.insert(name, ukey);
997 chunk_by_ukey.entry(ukey).or_insert_with(|| chunk);
998 (ukey, true)
999 }
1000 }
1001
1002 pub fn add_chunk(chunk_by_ukey: &mut ChunkByUkey) -> ChunkUkey {
1003 let chunk = Chunk::new(None, ChunkKind::Normal);
1004 let ukey = chunk.ukey();
1005 chunk_by_ukey.add(chunk);
1006 ukey
1007 }
1008
1009 #[instrument("Compilation:make",target=TRACING_BENCH_TARGET, skip_all)]
1010 pub async fn make(&mut self) -> Result<()> {
1011 if let Some(module_executor) = &mut self.module_executor {
1013 let mut module_executor = std::mem::take(module_executor);
1014 module_executor.hook_before_make(self).await?;
1015 self.module_executor = Some(module_executor);
1016 }
1017
1018 let artifact = std::mem::take(&mut self.make_artifact);
1019 self.make_artifact = make(self, artifact).await?;
1020
1021 self.in_finish_make.store(true, Ordering::Release);
1022
1023 Ok(())
1024 }
1025
1026 pub async fn rebuild_module<T>(
1027 &mut self,
1028 module_identifiers: IdentifierSet,
1029 f: impl Fn(Vec<&BoxModule>) -> T,
1030 ) -> Result<T> {
1031 let artifact = std::mem::take(&mut self.make_artifact);
1032
1033 self.module_graph_cache_artifact.unfreeze();
1035
1036 self.make_artifact = update_module_graph(
1037 self,
1038 artifact,
1039 vec![UpdateParam::ForceBuildModules(module_identifiers.clone())],
1040 )
1041 .await?;
1042
1043 let module_graph = self.get_module_graph();
1044 Ok(f(module_identifiers
1045 .into_iter()
1046 .filter_map(|id| module_graph.module_by_identifier(&id))
1047 .collect::<Vec<_>>()))
1048 }
1049
1050 #[instrument("Compilation:code_generation",target=TRACING_BENCH_TARGET, skip_all)]
1051 async fn code_generation(&mut self, modules: IdentifierSet) -> Result<()> {
1052 let logger = self.get_logger("rspack.Compilation");
1053 let mut codegen_cache_counter = match self.options.cache {
1054 CacheOptions::Disabled => None,
1055 _ => Some(logger.cache("module code generation cache")),
1056 };
1057
1058 let module_graph = self.get_module_graph();
1059 let mut no_codegen_dependencies_modules = IdentifierSet::default();
1060 let mut has_codegen_dependencies_modules = IdentifierSet::default();
1061 for module_identifier in modules {
1062 let module = module_graph
1063 .module_by_identifier(&module_identifier)
1064 .expect("should have module");
1065 if module.get_code_generation_dependencies().is_none() {
1066 no_codegen_dependencies_modules.insert(module_identifier);
1067 } else {
1068 has_codegen_dependencies_modules.insert(module_identifier);
1069 }
1070 }
1071
1072 self
1073 .code_generation_modules(&mut codegen_cache_counter, no_codegen_dependencies_modules)
1074 .await?;
1075 self
1076 .code_generation_modules(&mut codegen_cache_counter, has_codegen_dependencies_modules)
1077 .await?;
1078
1079 if let Some(counter) = codegen_cache_counter {
1080 logger.cache_end(counter);
1081 }
1082
1083 Ok(())
1084 }
1085
1086 pub(crate) async fn code_generation_modules(
1087 &mut self,
1088 cache_counter: &mut Option<CacheCount>,
1089 modules: IdentifierSet,
1090 ) -> Result<()> {
1091 let chunk_graph = &self.chunk_graph;
1092 let module_graph = self.get_module_graph();
1093 let mut jobs = Vec::new();
1094 for module in modules {
1095 let mut map: HashMap<RspackHashDigest, CodeGenerationJob> = HashMap::default();
1096 for runtime in chunk_graph.get_module_runtimes_iter(module, &self.chunk_by_ukey) {
1097 let hash = ChunkGraph::get_module_hash(self, module, runtime)
1098 .expect("should have cgm.hash in code generation");
1099 let scope = self
1100 .plugin_driver
1101 .compilation_hooks
1102 .concatenation_scope
1103 .call(self, module)
1104 .await?;
1105 if let Some(job) = map.get_mut(hash) {
1106 job.runtimes.push(runtime.clone());
1107 } else {
1108 map.insert(
1109 hash.clone(),
1110 CodeGenerationJob {
1111 module,
1112 hash: hash.clone(),
1113 runtime: runtime.clone(),
1114 runtimes: vec![runtime.clone()],
1115 scope,
1116 },
1117 );
1118 }
1119 }
1120 jobs.extend(map.into_values());
1121 }
1122
1123 let results = rspack_futures::scope::<_, _>(|token| {
1124 jobs.into_iter().for_each(|job| {
1125 let s = unsafe { token.used((&self, &module_graph, job)) };
1127
1128 s.spawn(|(this, module_graph, job)| async {
1129 let options = &this.options;
1130 let old_cache = &this.old_cache;
1131
1132 let module = module_graph
1133 .module_by_identifier(&job.module)
1134 .expect("should have module");
1135 let codegen_res = old_cache
1136 .code_generate_occasion
1137 .use_cache(&job, || async {
1138 module
1139 .code_generation(this, Some(&job.runtime), job.scope.clone())
1140 .await
1141 .map(|mut codegen_res| {
1142 codegen_res.set_hash(
1143 &options.output.hash_function,
1144 &options.output.hash_digest,
1145 &options.output.hash_salt,
1146 );
1147 codegen_res
1148 })
1149 })
1150 .await;
1151
1152 (job.module, job.runtimes, codegen_res)
1153 })
1154 })
1155 })
1156 .await;
1157 let results = results
1158 .into_iter()
1159 .map(|res| res.to_rspack_result())
1160 .collect::<Result<Vec<_>>>()?;
1161
1162 for (module, runtimes, (codegen_res, from_cache)) in results {
1163 if let Some(counter) = cache_counter {
1164 if from_cache {
1165 counter.hit();
1166 } else {
1167 counter.miss();
1168 }
1169 }
1170 let codegen_res = match codegen_res {
1171 Ok(codegen_res) => codegen_res,
1172 Err(err) => {
1173 let mut diagnostic = Diagnostic::from(err);
1174 diagnostic.module_identifier = Some(module);
1175 self.push_diagnostic(diagnostic);
1176 let mut codegen_res = CodeGenerationResult::default();
1177 codegen_res.set_hash(
1178 &self.options.output.hash_function,
1179 &self.options.output.hash_digest,
1180 &self.options.output.hash_salt,
1181 );
1182 codegen_res
1183 }
1184 };
1185 self
1186 .code_generation_results
1187 .insert(module, codegen_res, runtimes);
1188 self.code_generated_modules.insert(module);
1189 }
1190 Ok(())
1191 }
1192
1193 #[instrument("Compilation:create_module_assets",target=TRACING_BENCH_TARGET, skip_all)]
1194 async fn create_module_assets(&mut self, _plugin_driver: SharedPluginDriver) {
1195 let mut chunk_asset_map = vec![];
1196 let mut module_assets = vec![];
1197 let mg = self.get_module_graph();
1198 for (identifier, module) in mg.modules() {
1199 let assets = &module.build_info().assets;
1200 if assets.is_empty() {
1201 continue;
1202 }
1203
1204 for (name, asset) in assets.as_ref() {
1205 module_assets.push((name.clone(), asset.clone()));
1206 }
1207 if self
1209 .chunk_graph
1210 .chunk_graph_module_by_module_identifier
1211 .contains_key(&identifier)
1212 {
1213 for chunk in self.chunk_graph.get_module_chunks(identifier).iter() {
1214 for name in assets.keys() {
1215 chunk_asset_map.push((*chunk, name.clone()))
1216 }
1217 }
1218 }
1219 }
1220
1221 for (name, asset) in module_assets {
1222 self.emit_asset(name, asset);
1223 }
1224
1225 for (chunk, asset_name) in chunk_asset_map {
1226 let chunk = self.chunk_by_ukey.expect_get_mut(&chunk);
1227 chunk.add_auxiliary_file(asset_name);
1228 }
1229 }
1230
1231 #[instrument("Compilation::create_chunk_assets",target=TRACING_BENCH_TARGET, skip_all)]
1232 async fn create_chunk_assets(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> {
1233 if (self.options.output.filename.has_hash_placeholder()
1234 || self.options.output.chunk_filename.has_hash_placeholder()
1235 || self.options.output.css_filename.has_hash_placeholder()
1236 || self
1237 .options
1238 .output
1239 .css_chunk_filename
1240 .has_hash_placeholder())
1241 && let Some(diagnostic) = self.incremental.disable_passes(
1242 IncrementalPasses::CHUNKS_RENDER,
1243 "Chunk filename that dependent on full hash",
1244 "chunk filename that dependent on full hash is not supported in incremental compilation",
1245 )
1246 {
1247 if let Some(diagnostic) = diagnostic {
1248 self.push_diagnostic(diagnostic);
1249 }
1250 self.chunk_render_artifact.clear();
1251 }
1252
1253 let chunks = if let Some(mutations) = self
1254 .incremental
1255 .mutations_read(IncrementalPasses::CHUNKS_RENDER)
1256 && !self.chunk_render_artifact.is_empty()
1257 {
1258 let removed_chunks = mutations.iter().filter_map(|mutation| match mutation {
1259 Mutation::ChunkRemove { chunk } => Some(*chunk),
1260 _ => None,
1261 });
1262 for removed_chunk in removed_chunks {
1263 self.chunk_render_artifact.remove(&removed_chunk);
1264 }
1265 self
1266 .chunk_render_artifact
1267 .retain(|chunk, _| self.chunk_by_ukey.contains(chunk));
1268 let chunks: UkeySet<ChunkUkey> = mutations
1269 .iter()
1270 .filter_map(|mutation| match mutation {
1271 Mutation::ChunkSetHashes { chunk } => Some(*chunk),
1272 _ => None,
1273 })
1274 .collect();
1275 tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::CHUNKS_RENDER, %mutations);
1276 let logger = self.get_logger("rspack.incremental.chunksRender");
1277 logger.log(format!(
1278 "{} chunks are affected, {} in total",
1279 chunks.len(),
1280 self.chunk_by_ukey.len()
1281 ));
1282 chunks
1283 } else {
1284 self.chunk_by_ukey.keys().copied().collect()
1285 };
1286 let results = rspack_futures::scope::<_, Result<_>>(|token| {
1287 chunks.iter().for_each(|chunk| {
1288 let s = unsafe { token.used((&self, &plugin_driver, chunk)) };
1290
1291 s.spawn(|(this, plugin_driver, chunk)| async {
1292 let mut manifests = Vec::new();
1293 let mut diagnostics = Vec::new();
1294 plugin_driver
1295 .compilation_hooks
1296 .render_manifest
1297 .call(this, chunk, &mut manifests, &mut diagnostics)
1298 .await?;
1299
1300 rspack_error::Result::Ok((
1301 *chunk,
1302 ChunkRenderResult {
1303 manifests,
1304 diagnostics,
1305 },
1306 ))
1307 });
1308 })
1309 })
1310 .await;
1311
1312 let mut chunk_render_results: UkeyMap<ChunkUkey, ChunkRenderResult> = Default::default();
1313 for result in results {
1314 let item = result.to_rspack_result()?;
1315 let (key, value) = item?;
1316 chunk_render_results.insert(key, value);
1317 }
1318 let chunk_ukey_and_manifest = if self
1319 .incremental
1320 .passes_enabled(IncrementalPasses::CHUNKS_RENDER)
1321 {
1322 self.chunk_render_artifact.extend(chunk_render_results);
1323 self.chunk_render_artifact.clone()
1324 } else {
1325 chunk_render_results
1326 };
1327
1328 for (
1329 chunk_ukey,
1330 ChunkRenderResult {
1331 manifests,
1332 diagnostics,
1333 },
1334 ) in chunk_ukey_and_manifest
1335 {
1336 self.extend_diagnostics(diagnostics);
1337
1338 for file_manifest in manifests {
1339 let filename = file_manifest.filename;
1340 let current_chunk = self.chunk_by_ukey.expect_get_mut(&chunk_ukey);
1341
1342 current_chunk.set_rendered(true);
1343 if file_manifest.auxiliary {
1344 current_chunk.add_auxiliary_file(filename.clone());
1345 } else {
1346 current_chunk.add_file(filename.clone());
1347 }
1348
1349 self.emit_asset(
1350 filename.clone(),
1351 CompilationAsset::new(Some(file_manifest.source), file_manifest.info),
1352 );
1353
1354 _ = self
1355 .chunk_asset(chunk_ukey, &filename, plugin_driver.clone())
1356 .await;
1357 }
1358 }
1359
1360 Ok(())
1361 }
1362
1363 #[instrument("Compilation:process_assets",target=TRACING_BENCH_TARGET, skip_all)]
1364 async fn process_assets(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> {
1365 plugin_driver
1366 .compilation_hooks
1367 .process_assets
1368 .call(self)
1369 .await
1370 .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.processAssets"))
1371 }
1372
1373 #[instrument("Compilation:after_process_assets", skip_all)]
1374 async fn after_process_assets(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> {
1375 plugin_driver
1376 .compilation_hooks
1377 .after_process_assets
1378 .call(self)
1379 .await
1380 }
1381
1382 #[instrument("Compilation:after_seal", target=TRACING_BENCH_TARGET,skip_all)]
1383 async fn after_seal(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> {
1384 plugin_driver.compilation_hooks.after_seal.call(self).await
1385 }
1386
1387 async fn chunk_asset(
1392 &self,
1393 chunk_ukey: ChunkUkey,
1394 filename: &str,
1395 plugin_driver: SharedPluginDriver,
1396 ) -> Result<()> {
1397 plugin_driver
1398 .compilation_hooks
1399 .chunk_asset
1400 .call(self, &chunk_ukey, filename)
1401 .await?;
1402 Ok(())
1403 }
1404
1405 pub fn entry_modules(&self) -> IdentifierSet {
1406 let module_graph = self.get_module_graph();
1407 self
1408 .entries
1409 .values()
1410 .flat_map(|item| item.all_dependencies())
1411 .chain(self.global_entry.all_dependencies())
1412 .filter_map(|dep_id| {
1413 module_graph
1416 .module_identifier_by_dependency_id(dep_id)
1417 .copied()
1418 })
1419 .collect()
1420 }
1421
1422 pub fn entrypoint_by_name(&self, name: &str) -> &Entrypoint {
1423 let ukey = self.entrypoints.get(name).expect("entrypoint not found");
1424 self.chunk_group_by_ukey.expect_get(ukey)
1425 }
1426
1427 #[instrument("Compilation:finish",target=TRACING_BENCH_TARGET, skip_all)]
1428 pub async fn finish(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> {
1429 self.in_finish_make.store(false, Ordering::Release);
1430 let make_artifact = std::mem::take(&mut self.make_artifact);
1432 self.make_artifact = finish_make(self, make_artifact).await?;
1433
1434 let logger = self.get_logger("rspack.Compilation");
1435
1436 if let Some(module_executor) = &mut self.module_executor {
1438 let mut module_executor = std::mem::take(module_executor);
1439 module_executor.hook_after_finish_modules(self).await?;
1440 self.module_executor = Some(module_executor);
1441 }
1442
1443 if let Some(mutations) = self.incremental.mutations_write() {
1446 mutations.extend(
1447 self
1448 .make_artifact
1449 .affected_dependencies
1450 .updated()
1451 .iter()
1452 .map(|&dependency| Mutation::DependencyUpdate { dependency }),
1453 );
1454 mutations.extend(
1455 self
1456 .make_artifact
1457 .affected_modules
1458 .removed()
1459 .iter()
1460 .map(|&module| Mutation::ModuleRemove { module }),
1461 );
1462 mutations.extend(
1463 self
1464 .make_artifact
1465 .affected_modules
1466 .updated()
1467 .iter()
1468 .map(|&module| Mutation::ModuleUpdate { module }),
1469 );
1470 mutations.extend(
1471 self
1472 .make_artifact
1473 .affected_modules
1474 .added()
1475 .iter()
1476 .map(|&module| Mutation::ModuleAdd { module }),
1477 );
1478 tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::MAKE, %mutations);
1479 }
1480
1481 let start = logger.time("finish modules");
1482 plugin_driver
1487 .compilation_hooks
1488 .finish_modules
1489 .call(self)
1490 .await?;
1491
1492 logger.time_end(start);
1493
1494 self.module_graph_cache_artifact.freeze();
1496 self.collect_dependencies_diagnostics();
1500 self.module_graph_cache_artifact.unfreeze();
1501
1502 let diagnostics = self.make_artifact.diagnostics();
1504 self.extend_diagnostics(diagnostics);
1505 Ok(())
1506 }
1507
1508 #[tracing::instrument("Compilation:collect_dependencies_diagnostics", skip_all)]
1509 fn collect_dependencies_diagnostics(&mut self) {
1510 let mutations = self
1511 .incremental
1512 .mutations_read(IncrementalPasses::DEPENDENCIES_DIAGNOSTICS);
1513 let modules = if let Some(mutations) = mutations
1515 && !self.dependencies_diagnostics_artifact.is_empty()
1516 {
1517 let revoked_modules = mutations.iter().filter_map(|mutation| match mutation {
1518 Mutation::ModuleRemove { module } => Some(*module),
1519 _ => None,
1520 });
1521 for revoked_module in revoked_modules {
1522 self
1523 .dependencies_diagnostics_artifact
1524 .remove(&revoked_module);
1525 }
1526 let modules = mutations.get_affected_modules_with_module_graph(&self.get_module_graph());
1527 let logger = self.get_logger("rspack.incremental.dependenciesDiagnostics");
1528 logger.log(format!(
1529 "{} modules are affected, {} in total",
1530 modules.len(),
1531 self.get_module_graph().modules().len()
1532 ));
1533 modules
1534 } else {
1535 self.get_module_graph().modules().keys().copied().collect()
1536 };
1537 let module_graph = self.get_module_graph();
1538 let module_graph_cache = &self.module_graph_cache_artifact;
1539 let dependencies_diagnostics: DependenciesDiagnosticsArtifact = modules
1540 .par_iter()
1541 .map(|module_identifier| {
1542 let mgm = module_graph
1543 .module_graph_module_by_identifier(module_identifier)
1544 .expect("should have mgm");
1545 let diagnostics = mgm
1546 .all_dependencies
1547 .iter()
1548 .filter_map(|dependency_id| module_graph.dependency_by_id(dependency_id))
1549 .filter_map(|dependency| {
1550 dependency
1551 .get_diagnostics(&module_graph, module_graph_cache)
1552 .map(|diagnostics| {
1553 diagnostics.into_iter().map(|mut diagnostic| {
1554 diagnostic.module_identifier = Some(*module_identifier);
1555 diagnostic.loc = dependency.loc();
1556 diagnostic
1557 })
1558 })
1559 })
1560 .flatten()
1561 .collect::<Vec<_>>();
1562 (*module_identifier, diagnostics)
1563 })
1564 .collect();
1565 let all_modules_diagnostics = if mutations.is_some() {
1566 self
1567 .dependencies_diagnostics_artifact
1568 .extend(dependencies_diagnostics);
1569 self.dependencies_diagnostics_artifact.clone()
1570 } else {
1571 dependencies_diagnostics
1572 };
1573 self.extend_diagnostics(all_modules_diagnostics.into_values().flatten());
1574 }
1575
1576 #[instrument("Compilation:seal", skip_all)]
1577 pub async fn seal(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> {
1578 self.other_module_graph = Some(ModuleGraphPartial::default());
1579
1580 if !self.options.mode.is_development() {
1581 self.module_static_cache_artifact.freeze();
1582 }
1583
1584 let logger = self.get_logger("rspack.Compilation");
1585
1586 plugin_driver
1588 .compilation_hooks
1589 .seal
1590 .call(self)
1591 .await
1592 .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.seal"))?;
1593
1594 let start = logger.time("optimize dependencies");
1595 while matches!(
1598 plugin_driver
1599 .compilation_hooks
1600 .optimize_dependencies
1601 .call(self)
1602 .await
1603 .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeDependencies"))?,
1604 Some(true)
1605 ) {}
1606
1607 logger.time_end(start);
1608
1609 let start = logger.time("create chunks");
1613 self.module_graph_cache_artifact.freeze();
1614 use_code_splitting_cache(self, |compilation| async {
1615 let start = logger.time("rebuild chunk graph");
1616 if compilation.options.experiments.parallel_code_splitting {
1617 build_chunk_graph_new(compilation)?;
1618 } else {
1619 build_chunk_graph(compilation)?;
1620 }
1621 compilation
1622 .chunk_graph
1623 .generate_dot(compilation, "after-code-splitting")
1624 .await;
1625 logger.time_end(start);
1626 Ok(compilation)
1627 })
1628 .await?;
1629
1630 while matches!(
1631 plugin_driver
1632 .compilation_hooks
1633 .optimize_modules
1634 .call(self)
1635 .await
1636 .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeModules"))?,
1637 Some(true)
1638 ) {}
1639
1640 plugin_driver
1641 .compilation_hooks
1642 .after_optimize_modules
1643 .call(self)
1644 .await
1645 .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.afterOptimizeModules"))?;
1646
1647 while matches!(
1648 plugin_driver
1649 .compilation_hooks
1650 .optimize_chunks
1651 .call(self)
1652 .await
1653 .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeChunks"))?,
1654 Some(true)
1655 ) {}
1656
1657 logger.time_end(start);
1658
1659 let start = logger.time("optimize");
1660 plugin_driver
1661 .compilation_hooks
1662 .optimize_tree
1663 .call(self)
1664 .await
1665 .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeTree"))?;
1666
1667 plugin_driver
1668 .compilation_hooks
1669 .optimize_chunk_modules
1670 .call(self)
1671 .await
1672 .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeChunkModules"))?;
1673 logger.time_end(start);
1674
1675 let start = logger.time("module ids");
1679
1680 plugin_driver
1681 .compilation_hooks
1682 .module_ids
1683 .call(self)
1684 .await
1685 .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.moduleIds"))?;
1686 logger.time_end(start);
1687
1688 let start = logger.time("chunk ids");
1689 plugin_driver
1690 .compilation_hooks
1691 .chunk_ids
1692 .call(self)
1693 .await
1694 .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.chunkIds"))?;
1695 logger.time_end(start);
1696
1697 self.assign_runtime_ids();
1698
1699 let start = logger.time("optimize code generation");
1700 plugin_driver
1701 .compilation_hooks
1702 .optimize_code_generation
1703 .call(self)
1704 .await
1705 .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.optimizeCodeGeneration"))?;
1706 logger.time_end(start);
1707
1708 let create_module_hashes_modules = if let Some(mutations) = self
1709 .incremental
1710 .mutations_read(IncrementalPasses::MODULES_HASHES)
1711 && !self.cgm_hash_artifact.is_empty()
1712 {
1713 let revoked_modules = mutations.iter().filter_map(|mutation| match mutation {
1714 Mutation::ModuleRemove { module } => Some(*module),
1715 _ => None,
1716 });
1717 for revoked_module in revoked_modules {
1718 self.cgm_hash_artifact.remove(&revoked_module);
1719 }
1720 let mut modules = mutations.get_affected_modules_with_chunk_graph(self);
1721
1722 let mg = self.get_module_graph();
1724 for mi in mg.modules().keys() {
1725 let module_runtimes = self
1726 .chunk_graph
1727 .get_module_runtimes(*mi, &self.chunk_by_ukey);
1728 let module_runtime_keys = module_runtimes
1729 .values()
1730 .map(get_runtime_key)
1731 .collect::<HashSet<_>>();
1732
1733 if let Some(runtime_map) = self.cgm_hash_artifact.get_runtime_map(mi) {
1734 if module_runtimes.is_empty() {
1735 continue;
1737 }
1738 if module_runtimes.len() == 1 {
1739 if !matches!(runtime_map.mode, RuntimeMode::SingleEntry)
1741 || runtime_map
1742 .single_runtime
1743 .as_ref()
1744 .expect("should have single runtime for single entry")
1745 != module_runtimes
1746 .values()
1747 .next()
1748 .expect("should have at least one runtime")
1749 {
1750 modules.insert(*mi);
1751 }
1752 } else {
1753 if matches!(runtime_map.mode, RuntimeMode::SingleEntry) {
1755 modules.insert(*mi);
1756 continue;
1757 }
1758
1759 if runtime_map.map.len() != module_runtimes.len() {
1760 modules.insert(*mi);
1761 continue;
1762 }
1763
1764 for runtime_key in runtime_map.map.keys() {
1765 if !module_runtime_keys.contains(runtime_key) {
1766 modules.insert(*mi);
1767 break;
1768 }
1769 }
1770 }
1771 }
1772 }
1773
1774 tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::MODULES_HASHES, %mutations, ?modules);
1775 let logger = self.get_logger("rspack.incremental.modulesHashes");
1776 logger.log(format!(
1777 "{} modules are affected, {} in total",
1778 modules.len(),
1779 mg.modules().len()
1780 ));
1781
1782 modules
1783 } else {
1784 self.get_module_graph().modules().keys().copied().collect()
1785 };
1786 self
1787 .create_module_hashes(create_module_hashes_modules)
1788 .await?;
1789
1790 let start = logger.time("code generation");
1791 let code_generation_modules = if let Some(mutations) = self
1792 .incremental
1793 .mutations_read(IncrementalPasses::MODULES_CODEGEN)
1794 && !self.code_generation_results.is_empty()
1795 {
1796 let revoked_modules = mutations.iter().filter_map(|mutation| match mutation {
1797 Mutation::ModuleRemove { module } => Some(*module),
1798 _ => None,
1799 });
1800 for revoked_module in revoked_modules {
1801 self.code_generation_results.remove(&revoked_module);
1802 }
1803 let modules: IdentifierSet = mutations
1804 .iter()
1805 .filter_map(|mutation| match mutation {
1806 Mutation::ModuleSetHashes { module } => Some(*module),
1807 _ => None,
1808 })
1809 .collect();
1810 for module in &modules {
1812 self.code_generation_results.remove(module);
1813 }
1814 tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::MODULES_CODEGEN, %mutations);
1815 let logger = self.get_logger("rspack.incremental.modulesCodegen");
1816 logger.log(format!(
1817 "{} modules are affected, {} in total",
1818 modules.len(),
1819 self.get_module_graph().modules().len()
1820 ));
1821 modules
1822 } else {
1823 self.get_module_graph().modules().keys().copied().collect()
1824 };
1825 self.code_generation(code_generation_modules).await?;
1826
1827 plugin_driver
1828 .compilation_hooks
1829 .after_code_generation
1830 .call(self)
1831 .await
1832 .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.afterCodeGeneration"))?;
1833 logger.time_end(start);
1834
1835 let start = logger.time("runtime requirements");
1836 let process_runtime_requirements_modules = if let Some(mutations) = self
1837 .incremental
1838 .mutations_read(IncrementalPasses::MODULES_RUNTIME_REQUIREMENTS)
1839 && !self.cgm_runtime_requirements_artifact.is_empty()
1840 {
1841 let revoked_modules = mutations.iter().filter_map(|mutation| match mutation {
1842 Mutation::ModuleRemove { module } => Some(*module),
1843 _ => None,
1844 });
1845 for revoked_module in revoked_modules {
1846 self
1847 .cgm_runtime_requirements_artifact
1848 .remove(&revoked_module);
1849 }
1850 let modules: IdentifierSet = mutations
1851 .iter()
1852 .filter_map(|mutation| match mutation {
1853 Mutation::ModuleSetHashes { module } => Some(*module),
1854 _ => None,
1855 })
1856 .collect();
1857 let logger = self.get_logger("rspack.incremental.modulesRuntimeRequirements");
1858 logger.log(format!(
1859 "{} modules are affected, {} in total",
1860 modules.len(),
1861 self.get_module_graph().modules().len()
1862 ));
1863 modules
1864 } else {
1865 self.get_module_graph().modules().keys().copied().collect()
1866 };
1867 self
1868 .process_modules_runtime_requirements(
1869 process_runtime_requirements_modules,
1870 plugin_driver.clone(),
1871 )
1872 .await?;
1873 let runtime_chunks = self.get_chunk_graph_entries().collect();
1874 let process_runtime_requirements_chunks = if let Some(mutations) = self
1875 .incremental
1876 .mutations_read(IncrementalPasses::CHUNKS_RUNTIME_REQUIREMENTS)
1877 && !self.cgc_runtime_requirements_artifact.is_empty()
1878 {
1879 let removed_chunks = mutations.iter().filter_map(|mutation| match mutation {
1880 Mutation::ChunkRemove { chunk } => Some(chunk),
1881 _ => None,
1882 });
1883 for removed_chunk in removed_chunks {
1884 self.cgc_runtime_requirements_artifact.remove(removed_chunk);
1885 }
1886 let affected_chunks = mutations.get_affected_chunks_with_chunk_graph(self);
1887 for affected_chunk in &affected_chunks {
1888 self
1889 .cgc_runtime_requirements_artifact
1890 .remove(affected_chunk);
1891 }
1892 for runtime_chunk in &runtime_chunks {
1893 self.cgc_runtime_requirements_artifact.remove(runtime_chunk);
1894 }
1895 self
1896 .cgc_runtime_requirements_artifact
1897 .retain(|chunk, _| self.chunk_by_ukey.contains(chunk));
1898 let logger = self.get_logger("rspack.incremental.chunksRuntimeRequirements");
1899 logger.log(format!(
1900 "{} chunks are affected, {} in total",
1901 affected_chunks.len(),
1902 self.chunk_by_ukey.len()
1903 ));
1904 affected_chunks
1905 } else {
1906 self.chunk_by_ukey.keys().copied().collect()
1907 };
1908 self
1909 .process_chunks_runtime_requirements(
1910 process_runtime_requirements_chunks,
1911 runtime_chunks,
1912 plugin_driver.clone(),
1913 )
1914 .await?;
1915 logger.time_end(start);
1916
1917 let start = logger.time("hashing");
1918 self.create_hash(plugin_driver.clone()).await?;
1919 self.runtime_modules_code_generation().await?;
1920 logger.time_end(start);
1921
1922 let start = logger.time("create module assets");
1923 self.create_module_assets(plugin_driver.clone()).await;
1924 logger.time_end(start);
1925
1926 let start = logger.time("create chunk assets");
1927 self.create_chunk_assets(plugin_driver.clone()).await?;
1928 logger.time_end(start);
1929
1930 let start = logger.time("process assets");
1931 self.process_assets(plugin_driver.clone()).await?;
1932 logger.time_end(start);
1933
1934 let start = logger.time("after process assets");
1935 self.after_process_assets(plugin_driver.clone()).await?;
1936 logger.time_end(start);
1937
1938 let start = logger.time("after seal");
1939 self.after_seal(plugin_driver).await?;
1940 logger.time_end(start);
1941
1942 if !self.options.mode.is_development() {
1943 self.module_static_cache_artifact.unfreeze();
1944 }
1945 Ok(())
1946 }
1947
1948 pub fn assign_runtime_ids(&mut self) {
1949 fn process_entrypoint(
1950 entrypoint_ukey: &ChunkGroupUkey,
1951 chunk_group_by_ukey: &ChunkGroupByUkey,
1952 chunk_by_ukey: &ChunkByUkey,
1953 chunk_ids: &ChunkIdsArtifact,
1954 chunk_graph: &mut ChunkGraph,
1955 ) {
1956 let entrypoint = chunk_group_by_ukey.expect_get(entrypoint_ukey);
1957 let runtime = entrypoint
1958 .kind
1959 .get_entry_options()
1960 .and_then(|o| match &o.runtime {
1961 Some(EntryRuntime::String(s)) => Some(s.to_owned()),
1962 _ => None,
1963 })
1964 .or(entrypoint.name().map(|n| n.to_string()));
1965 if let (Some(runtime), Some(chunk)) = (
1966 runtime,
1967 chunk_by_ukey.get(&entrypoint.get_runtime_chunk(chunk_group_by_ukey)),
1968 ) {
1969 chunk_graph.set_runtime_id(runtime, chunk.id(chunk_ids).map(|id| id.to_string()));
1970 }
1971 }
1972 for i in self.entrypoints.iter() {
1973 process_entrypoint(
1974 i.1,
1975 &self.chunk_group_by_ukey,
1976 &self.chunk_by_ukey,
1977 &self.chunk_ids_artifact,
1978 &mut self.chunk_graph,
1979 )
1980 }
1981 for i in self.async_entrypoints.iter() {
1982 process_entrypoint(
1983 i,
1984 &self.chunk_group_by_ukey,
1985 &self.chunk_by_ukey,
1986 &self.chunk_ids_artifact,
1987 &mut self.chunk_graph,
1988 )
1989 }
1990 }
1991
1992 pub fn get_chunk_graph_entries(&self) -> impl Iterator<Item = ChunkUkey> + use<'_> {
1993 let entries = self.entrypoints.values().map(|entrypoint_ukey| {
1994 let entrypoint = self.chunk_group_by_ukey.expect_get(entrypoint_ukey);
1995 entrypoint.get_runtime_chunk(&self.chunk_group_by_ukey)
1996 });
1997 let async_entries = self.async_entrypoints.iter().map(|entrypoint_ukey| {
1998 let entrypoint = self.chunk_group_by_ukey.expect_get(entrypoint_ukey);
1999 entrypoint.get_runtime_chunk(&self.chunk_group_by_ukey)
2000 });
2001 entries.chain(async_entries)
2002 }
2003
2004 #[instrument("Compilation:process_modules_runtime_requirements", skip_all)]
2005 pub async fn process_modules_runtime_requirements(
2006 &mut self,
2007 modules: IdentifierSet,
2008 plugin_driver: SharedPluginDriver,
2009 ) -> Result<()> {
2010 let logger = self.get_logger("rspack.Compilation");
2011 let start = logger.time("runtime requirements.modules");
2012
2013 let module_results = rspack_futures::scope::<_, Result<_>>(|token| {
2014 modules
2015 .into_iter()
2016 .filter(|module| self.chunk_graph.get_number_of_module_chunks(*module) > 0)
2017 .for_each(|module| {
2018 let s = unsafe { token.used((&self, &plugin_driver)) };
2019 s.spawn(move |(compilation, plugin_driver)| async move {
2020 let mut map = RuntimeSpecMap::new();
2021 let runtimes = compilation
2022 .chunk_graph
2023 .get_module_runtimes_iter(module, &compilation.chunk_by_ukey);
2024 for runtime in runtimes {
2025 let runtime_requirements = compilation
2026 .old_cache
2027 .process_runtime_requirements_occasion
2028 .use_cache(module, runtime, compilation, || async {
2029 let mut runtime_requirements = compilation
2030 .code_generation_results
2031 .get_runtime_requirements(&module, Some(runtime));
2032
2033 plugin_driver
2034 .compilation_hooks
2035 .additional_module_runtime_requirements
2036 .call(compilation, &module, &mut runtime_requirements)
2037 .await
2038 .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.additionalModuleRuntimeRequirements"))?;
2039
2040 compilation
2041 .process_runtime_requirement_hook(&mut runtime_requirements, {
2042 let plugin_driver = plugin_driver.clone();
2043 move |compilation,
2044 all_runtime_requirements,
2045 runtime_requirements,
2046 runtime_requirements_mut| {
2047 Box::pin({
2048 let plugin_driver = plugin_driver.clone();
2049 async move {
2050 plugin_driver
2051 .compilation_hooks
2052 .runtime_requirement_in_module
2053 .call(
2054 compilation,
2055 &module,
2056 all_runtime_requirements,
2057 runtime_requirements,
2058 runtime_requirements_mut,
2059 )
2060 .await
2061 .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.runtimeRequirementInModule"))?;
2062 Ok(())
2063 }})
2064 }
2065 })
2066 .await?;
2067 Ok(runtime_requirements)
2068 })
2069 .await?;
2070 map.set(runtime.clone(), runtime_requirements);
2071 }
2072 Ok((module, map))
2073 });
2074 });
2075 })
2076 .await
2077 .into_iter()
2078 .map(|r| r.to_rspack_result())
2079 .collect::<Result<Vec<_>>>()?;
2080
2081 for entry in module_results {
2082 let (module, map) = entry?;
2083 ChunkGraph::set_module_runtime_requirements(self, module, map);
2084 }
2085 logger.time_end(start);
2086 Ok(())
2087 }
2088
2089 #[instrument(name = "Compilation:process_chunks_runtime_requirements", target=TRACING_BENCH_TARGET skip_all)]
2090 pub async fn process_chunks_runtime_requirements(
2091 &mut self,
2092 chunks: UkeySet<ChunkUkey>,
2093 entries: UkeySet<ChunkUkey>,
2094 plugin_driver: SharedPluginDriver,
2095 ) -> Result<()> {
2096 let logger = self.get_logger("rspack.Compilation");
2097 let start = logger.time("runtime requirements.chunks");
2098 let chunk_requirements = chunks
2099 .iter()
2100 .chain(entries.iter())
2101 .par_bridge()
2102 .map(|chunk_ukey| {
2103 let mut set = RuntimeGlobals::default();
2104 for mid in self.chunk_graph.get_chunk_modules_identifier(chunk_ukey) {
2105 let chunk = self.chunk_by_ukey.expect_get(chunk_ukey);
2106 if let Some(runtime_requirements) =
2107 ChunkGraph::get_module_runtime_requirements(self, *mid, chunk.runtime())
2108 {
2109 set.insert(*runtime_requirements);
2110 }
2111 }
2112
2113 (*chunk_ukey, set)
2114 })
2115 .collect::<UkeyMap<_, _>>();
2116
2117 for (chunk_ukey, mut set) in chunk_requirements {
2118 plugin_driver
2119 .compilation_hooks
2120 .additional_chunk_runtime_requirements
2121 .call(self, &chunk_ukey, &mut set)
2122 .await
2123 .map_err(|e| {
2124 e.wrap_err("caused by plugins in Compilation.hooks.additionalChunkRuntimeRequirements")
2125 })?;
2126
2127 self
2128 .process_runtime_requirement_hook_mut(&mut set, {
2129 let plugin_driver = plugin_driver.clone();
2130 move |compilation,
2131 all_runtime_requirements,
2132 runtime_requirements,
2133 runtime_requirements_mut| {
2134 Box::pin({
2135 let plugin_driver = plugin_driver.clone();
2136 async move {
2137 plugin_driver
2138 .compilation_hooks
2139 .runtime_requirement_in_chunk
2140 .call(
2141 compilation,
2142 &chunk_ukey,
2143 all_runtime_requirements,
2144 runtime_requirements,
2145 runtime_requirements_mut,
2146 )
2147 .await
2148 .map_err(|e| {
2149 e.wrap_err("caused by plugins in Compilation.hooks.runtimeRequirementInChunk")
2150 })?;
2151 Ok(())
2152 }
2153 })
2154 }
2155 })
2156 .await?;
2157
2158 ChunkGraph::set_chunk_runtime_requirements(self, chunk_ukey, set);
2159 }
2160 logger.time_end(start);
2161
2162 let start = logger.time("runtime requirements.entries");
2163 for &entry_ukey in &entries {
2164 let entry = self.chunk_by_ukey.expect_get(&entry_ukey);
2165 let mut set = RuntimeGlobals::default();
2166 for chunk_ukey in entry
2167 .get_all_referenced_chunks(&self.chunk_group_by_ukey)
2168 .iter()
2169 {
2170 let runtime_requirements = ChunkGraph::get_chunk_runtime_requirements(self, chunk_ukey);
2171 set.insert(*runtime_requirements);
2172 }
2173
2174 plugin_driver
2175 .compilation_hooks
2176 .additional_tree_runtime_requirements
2177 .call(self, &entry_ukey, &mut set)
2178 .await
2179 .map_err(|e| {
2180 e.wrap_err("caused by plugins in Compilation.hooks.additionalTreeRuntimeRequirements")
2181 })?;
2182
2183 self
2184 .process_runtime_requirement_hook_mut(&mut set, {
2185 let plugin_driver = plugin_driver.clone();
2186 move |compilation,
2187 all_runtime_requirements,
2188 runtime_requirements,
2189 runtime_requirements_mut| {
2190 Box::pin({
2191 let plugin_driver = plugin_driver.clone();
2192 async move {
2193 plugin_driver
2194 .compilation_hooks
2195 .runtime_requirement_in_tree
2196 .call(
2197 compilation,
2198 &entry_ukey,
2199 all_runtime_requirements,
2200 runtime_requirements,
2201 runtime_requirements_mut,
2202 )
2203 .await
2204 .map_err(|e| {
2205 e.wrap_err("caused by plugins in Compilation.hooks.runtimeRequirementInTree")
2206 })?;
2207 Ok(())
2208 }
2209 })
2210 }
2211 })
2212 .await?;
2213
2214 ChunkGraph::set_tree_runtime_requirements(self, entry_ukey, set);
2215 }
2216
2217 for entry_ukey in &entries {
2221 let runtime_module_ids: Vec<_> = self
2222 .chunk_graph
2223 .get_chunk_runtime_modules_iterable(entry_ukey)
2224 .copied()
2225 .collect();
2226 for runtime_module_id in runtime_module_ids {
2227 plugin_driver
2228 .compilation_hooks
2229 .runtime_module
2230 .call(self, &runtime_module_id, entry_ukey)
2231 .await
2232 .map_err(|e| e.wrap_err("caused by plugins in Compilation.hooks.runtimeModule"))?;
2233 }
2234 }
2235
2236 logger.time_end(start);
2237 Ok(())
2238 }
2239
2240 process_runtime_requirement_hook_macro!(
2241 process_runtime_requirement_hook,
2242 &Compilation,
2243 &'a Compilation
2244 );
2245 process_runtime_requirement_hook_macro!(
2246 process_runtime_requirement_hook_mut,
2247 &mut Compilation,
2248 &'a mut Compilation
2249 );
2250
2251 #[instrument(name = "Compilation:create_hash",target=TRACING_BENCH_TARGET, skip_all)]
2252 pub async fn create_hash(&mut self, plugin_driver: SharedPluginDriver) -> Result<()> {
2253 let logger = self.get_logger("rspack.Compilation");
2254
2255 let mut full_hash_chunks = UkeySet::default();
2260 for chunk_ukey in self.chunk_by_ukey.keys() {
2261 let chunk_dependent_full_hash = plugin_driver
2262 .compilation_hooks
2263 .dependent_full_hash
2264 .call(self, chunk_ukey)
2265 .await?
2266 .unwrap_or_default();
2267 if chunk_dependent_full_hash {
2268 full_hash_chunks.insert(*chunk_ukey);
2269 }
2270 }
2271 if !full_hash_chunks.is_empty()
2272 && let Some(diagnostic) = self.incremental.disable_passes(
2273 IncrementalPasses::CHUNKS_HASHES,
2274 "Chunk content that dependent on full hash",
2275 "it requires calculating the hashes of all the chunks, which is a global effect",
2276 )
2277 {
2278 if let Some(diagnostic) = diagnostic {
2279 self.push_diagnostic(diagnostic);
2280 }
2281 self.chunk_hashes_artifact.clear();
2282 }
2283
2284 let create_hash_chunks = if let Some(mutations) = self
2285 .incremental
2286 .mutations_read(IncrementalPasses::CHUNKS_HASHES)
2287 && !self.chunk_hashes_artifact.is_empty()
2288 {
2289 let removed_chunks = mutations.iter().filter_map(|mutation| match mutation {
2290 Mutation::ChunkRemove { chunk } => Some(*chunk),
2291 _ => None,
2292 });
2293 for removed_chunk in removed_chunks {
2294 self.chunk_hashes_artifact.remove(&removed_chunk);
2295 }
2296 self
2297 .chunk_hashes_artifact
2298 .retain(|chunk, _| self.chunk_by_ukey.contains(chunk));
2299 let chunks = mutations.get_affected_chunks_with_chunk_graph(self);
2300 tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::CHUNKS_HASHES, %mutations, ?chunks);
2301 let logger = self.get_logger("rspack.incremental.chunksHashes");
2302 logger.log(format!(
2303 "{} chunks are affected, {} in total",
2304 chunks.len(),
2305 self.chunk_by_ukey.len(),
2306 ));
2307 chunks
2308 } else {
2309 self.chunk_by_ukey.keys().copied().collect()
2310 };
2311
2312 let mut compilation_hasher = RspackHash::from(&self.options.output);
2313
2314 fn try_process_chunk_hash_results(
2315 compilation: &mut Compilation,
2316 chunk_hash_results: Vec<Result<(ChunkUkey, ChunkHashResult)>>,
2317 ) -> Result<()> {
2318 for hash_result in chunk_hash_results {
2319 let (chunk_ukey, chunk_hash_result) = hash_result?;
2320 let chunk = compilation.chunk_by_ukey.expect_get(&chunk_ukey);
2321 let chunk_hashes_changed = chunk.set_hashes(
2322 &mut compilation.chunk_hashes_artifact,
2323 chunk_hash_result.hash,
2324 chunk_hash_result.content_hash,
2325 );
2326 if chunk_hashes_changed && let Some(mutations) = compilation.incremental.mutations_write() {
2327 mutations.add(Mutation::ChunkSetHashes { chunk: chunk_ukey });
2328 }
2329 }
2330 Ok(())
2331 }
2332
2333 let unordered_runtime_chunks: UkeySet<ChunkUkey> = self.get_chunk_graph_entries().collect();
2334 let start = logger.time("hashing: hash chunks");
2335 let other_chunks: Vec<_> = create_hash_chunks
2336 .iter()
2337 .filter(|key| !unordered_runtime_chunks.contains(key))
2338 .collect();
2339
2340 let other_chunk_runtime_module_hashes = rspack_futures::scope::<_, Result<_>>(|token| {
2342 other_chunks
2343 .iter()
2344 .flat_map(|chunk| self.chunk_graph.get_chunk_runtime_modules_iterable(chunk))
2345 .for_each(|runtime_module_identifier| {
2346 let s = unsafe { token.used((&self, runtime_module_identifier)) };
2347 s.spawn(|(compilation, runtime_module_identifier)| async {
2348 let runtime_module = &compilation.runtime_modules[runtime_module_identifier];
2349 let digest = runtime_module.get_runtime_hash(compilation, None).await?;
2350 Ok((*runtime_module_identifier, digest))
2351 });
2352 })
2353 })
2354 .await
2355 .into_iter()
2356 .map(|res| res.to_rspack_result())
2357 .collect::<Result<Vec<_>>>()?;
2358
2359 for res in other_chunk_runtime_module_hashes {
2360 let (runtime_module_identifier, digest) = res?;
2361 self
2362 .runtime_modules_hash
2363 .insert(runtime_module_identifier, digest);
2364 }
2365
2366 let other_chunks_hash_results = rspack_futures::scope::<_, Result<_>>(|token| {
2368 for chunk in other_chunks {
2369 let s = unsafe { token.used((&self, chunk, &plugin_driver)) };
2370 s.spawn(|(compilation, chunk, plugin_driver)| async {
2371 let hash_result = compilation
2372 .process_chunk_hash(*chunk, plugin_driver)
2373 .await?;
2374 Ok((*chunk, hash_result))
2375 });
2376 }
2377 })
2378 .await
2379 .into_iter()
2380 .map(|res| res.to_rspack_result())
2381 .collect::<Result<Vec<_>>>()?;
2382
2383 try_process_chunk_hash_results(self, other_chunks_hash_results)?;
2384 logger.time_end(start);
2385
2386 let mut runtime_chunks_map: HashMap<ChunkUkey, (Vec<ChunkUkey>, u32)> =
2388 unordered_runtime_chunks
2389 .into_iter()
2390 .map(|runtime_chunk| (runtime_chunk, (Vec::new(), 0)))
2391 .collect();
2392 let mut remaining: u32 = 0;
2393 for runtime_chunk_ukey in runtime_chunks_map.keys().copied().collect::<Vec<_>>() {
2394 let runtime_chunk = self.chunk_by_ukey.expect_get(&runtime_chunk_ukey);
2395 let groups = runtime_chunk.get_all_referenced_async_entrypoints(&self.chunk_group_by_ukey);
2396 for other in groups
2397 .into_iter()
2398 .map(|group| self.chunk_group_by_ukey.expect_get(&group))
2399 .map(|group| group.get_runtime_chunk(&self.chunk_group_by_ukey))
2400 {
2401 let (other_referenced_by, _) = runtime_chunks_map
2402 .get_mut(&other)
2403 .expect("should in runtime_chunks_map");
2404 other_referenced_by.push(runtime_chunk_ukey);
2405 let info = runtime_chunks_map
2406 .get_mut(&runtime_chunk_ukey)
2407 .expect("should in runtime_chunks_map");
2408 info.1 += 1;
2409 remaining += 1;
2410 }
2411 }
2412 let mut runtime_chunks = Vec::with_capacity(runtime_chunks_map.len());
2414 for (runtime_chunk, (_, remaining)) in &runtime_chunks_map {
2415 if *remaining == 0 {
2416 runtime_chunks.push(*runtime_chunk);
2417 }
2418 }
2419 let mut ready_chunks = Vec::new();
2420
2421 let mut i = 0;
2422 while i < runtime_chunks.len() {
2423 let chunk_ukey = runtime_chunks[i];
2424 let has_full_hash_modules = full_hash_chunks.contains(&chunk_ukey)
2425 || self
2426 .chunk_graph
2427 .has_chunk_full_hash_modules(&chunk_ukey, &self.runtime_modules);
2428 if has_full_hash_modules {
2429 full_hash_chunks.insert(chunk_ukey);
2430 }
2431 let referenced_by = runtime_chunks_map
2432 .get(&chunk_ukey)
2433 .expect("should in runtime_chunks_map")
2434 .0
2435 .clone();
2436 for other in referenced_by {
2437 if has_full_hash_modules {
2438 for runtime_module in self.chunk_graph.get_chunk_runtime_modules_iterable(&other) {
2439 let runtime_module = self
2440 .runtime_modules
2441 .get(runtime_module)
2442 .expect("should have runtime_module");
2443 if runtime_module.dependent_hash() {
2444 full_hash_chunks.insert(other);
2445 break;
2446 }
2447 }
2448 }
2449 remaining -= 1;
2450 let (_, other_remaining) = runtime_chunks_map
2451 .get_mut(&other)
2452 .expect("should in runtime_chunks_map");
2453 *other_remaining -= 1;
2454 if *other_remaining == 0 {
2455 ready_chunks.push(other);
2456 }
2457 }
2458 if !ready_chunks.is_empty() {
2459 runtime_chunks.append(&mut ready_chunks);
2460 }
2461 i += 1;
2462 }
2463 if remaining > 0 {
2465 let mut circular: Vec<_> = runtime_chunks_map
2466 .iter()
2467 .filter(|(_, (_, remaining))| *remaining != 0)
2468 .map(|(chunk_ukey, _)| self.chunk_by_ukey.expect_get(chunk_ukey))
2469 .collect();
2470 circular.sort_unstable_by(|a, b| {
2471 a.id(&self.chunk_ids_artifact)
2472 .cmp(&b.id(&self.chunk_ids_artifact))
2473 });
2474 runtime_chunks.extend(circular.iter().map(|chunk| chunk.ukey()));
2475 let circular_names = circular
2476 .iter()
2477 .map(|chunk| {
2478 chunk
2479 .name()
2480 .or(chunk.id(&self.chunk_ids_artifact).map(|id| id.as_str()))
2481 .unwrap_or("no id chunk")
2482 })
2483 .join(", ");
2484 let error = rspack_error::Error::warning(format!(
2485 "Circular dependency between chunks with runtime ({circular_names})\nThis prevents using hashes of each other and should be avoided."
2486 ));
2487 self.push_diagnostic(error.into());
2488 }
2489
2490 let start = logger.time("hashing: hash runtime chunks");
2495 for runtime_chunk_ukey in runtime_chunks {
2496 let runtime_module_hashes = rspack_futures::scope::<_, Result<_>>(|token| {
2497 self
2498 .chunk_graph
2499 .get_chunk_runtime_modules_iterable(&runtime_chunk_ukey)
2500 .for_each(|runtime_module_identifier| {
2501 let s = unsafe { token.used((&self, runtime_module_identifier)) };
2502 s.spawn(|(compilation, runtime_module_identifier)| async {
2503 let runtime_module = &compilation.runtime_modules[runtime_module_identifier];
2504 let digest = runtime_module.get_runtime_hash(compilation, None).await?;
2505 Ok((*runtime_module_identifier, digest))
2506 });
2507 })
2508 })
2509 .await
2510 .into_iter()
2511 .map(|res| res.to_rspack_result())
2512 .collect::<Result<Vec<_>>>()?;
2513
2514 for res in runtime_module_hashes {
2515 let (mid, digest) = res?;
2516 self.runtime_modules_hash.insert(mid, digest);
2517 }
2518
2519 let chunk_hash_result = self
2520 .process_chunk_hash(runtime_chunk_ukey, &plugin_driver)
2521 .await?;
2522 let chunk = self.chunk_by_ukey.expect_get(&runtime_chunk_ukey);
2523 let chunk_hashes_changed = chunk.set_hashes(
2524 &mut self.chunk_hashes_artifact,
2525 chunk_hash_result.hash,
2526 chunk_hash_result.content_hash,
2527 );
2528 if chunk_hashes_changed && let Some(mutations) = self.incremental.mutations_write() {
2529 mutations.add(Mutation::ChunkSetHashes {
2530 chunk: runtime_chunk_ukey,
2531 });
2532 }
2533 }
2534 logger.time_end(start);
2535
2536 self
2538 .chunk_by_ukey
2539 .values()
2540 .sorted_unstable_by_key(|chunk| chunk.ukey())
2541 .filter_map(|chunk| chunk.hash(&self.chunk_hashes_artifact))
2542 .for_each(|hash| {
2543 hash.hash(&mut compilation_hasher);
2544 });
2545 self.hot_index.hash(&mut compilation_hasher);
2546 self.hash = Some(compilation_hasher.digest(&self.options.output.hash_digest));
2547
2548 let start = logger.time("hashing: process full hash chunks");
2550 for chunk_ukey in full_hash_chunks {
2551 for runtime_module_identifier in self
2552 .chunk_graph
2553 .get_chunk_runtime_modules_iterable(&chunk_ukey)
2554 {
2555 let runtime_module = &self.runtime_modules[runtime_module_identifier];
2556 if runtime_module.full_hash() || runtime_module.dependent_hash() {
2557 let digest = runtime_module.get_runtime_hash(self, None).await?;
2558 self
2559 .runtime_modules_hash
2560 .insert(*runtime_module_identifier, digest);
2561 }
2562 }
2563 let chunk = self.chunk_by_ukey.expect_get(&chunk_ukey);
2564 let new_chunk_hash = {
2565 let chunk_hash = chunk
2566 .hash(&self.chunk_hashes_artifact)
2567 .expect("should have chunk hash");
2568 let mut hasher = RspackHash::from(&self.options.output);
2569 chunk_hash.hash(&mut hasher);
2570 self.hash.hash(&mut hasher);
2571 hasher.digest(&self.options.output.hash_digest)
2572 };
2573 let new_content_hash = {
2574 let content_hash = chunk
2575 .content_hash(&self.chunk_hashes_artifact)
2576 .expect("should have content hash");
2577 content_hash
2578 .iter()
2579 .map(|(source_type, content_hash)| {
2580 let mut hasher = RspackHash::from(&self.options.output);
2581 content_hash.hash(&mut hasher);
2582 self.hash.hash(&mut hasher);
2583 (
2584 *source_type,
2585 hasher.digest(&self.options.output.hash_digest),
2586 )
2587 })
2588 .collect()
2589 };
2590 let chunk_hashes_changed = chunk.set_hashes(
2591 &mut self.chunk_hashes_artifact,
2592 new_chunk_hash,
2593 new_content_hash,
2594 );
2595 if chunk_hashes_changed && let Some(mutations) = self.incremental.mutations_write() {
2596 mutations.add(Mutation::ChunkSetHashes { chunk: chunk_ukey });
2597 }
2598 }
2599 logger.time_end(start);
2600 Ok(())
2601 }
2602
2603 #[instrument(skip_all)]
2604 pub async fn runtime_modules_code_generation(&mut self) -> Result<()> {
2605 let results = rspack_futures::scope::<_, Result<_>>(|token| {
2606 self
2607 .runtime_modules
2608 .iter()
2609 .for_each(|(runtime_module_identifier, runtime_module)| {
2610 let s = unsafe { token.used((&self, runtime_module_identifier, runtime_module)) };
2611 s.spawn(
2612 |(compilation, runtime_module_identifier, runtime_module)| async {
2613 let result = runtime_module
2614 .code_generation(compilation, None, None)
2615 .await?;
2616 let source = result
2617 .get(&SourceType::Runtime)
2618 .expect("should have source");
2619 Ok((*runtime_module_identifier, source.clone()))
2620 },
2621 )
2622 })
2623 })
2624 .await
2625 .into_iter()
2626 .map(|res| res.to_rspack_result())
2627 .collect::<Result<Vec<_>>>()?;
2628
2629 let mut runtime_module_sources = IdentifierMap::<BoxSource>::default();
2630 for result in results {
2631 let (runtime_module_identifier, source) = result?;
2632 runtime_module_sources.insert(runtime_module_identifier, source);
2633 }
2634
2635 self.runtime_modules_code_generation_source = runtime_module_sources;
2636 self
2637 .code_generated_modules
2638 .extend(self.runtime_modules.keys().copied());
2639 Ok(())
2640 }
2641
2642 async fn process_chunk_hash(
2643 &self,
2644 chunk_ukey: ChunkUkey,
2645 plugin_driver: &SharedPluginDriver,
2646 ) -> Result<ChunkHashResult> {
2647 let mut hasher = RspackHash::from(&self.options.output);
2648 if let Some(chunk) = self.chunk_by_ukey.get(&chunk_ukey) {
2649 chunk.update_hash(&mut hasher, self);
2650 }
2651 plugin_driver
2652 .compilation_hooks
2653 .chunk_hash
2654 .call(self, &chunk_ukey, &mut hasher)
2655 .await?;
2656 let chunk_hash = hasher.digest(&self.options.output.hash_digest);
2657
2658 let mut content_hashes: HashMap<SourceType, RspackHash> = HashMap::default();
2659 plugin_driver
2660 .compilation_hooks
2661 .content_hash
2662 .call(self, &chunk_ukey, &mut content_hashes)
2663 .await?;
2664
2665 let content_hashes = content_hashes
2666 .into_iter()
2667 .map(|(t, mut hasher)| {
2668 chunk_hash.hash(&mut hasher);
2669 (t, hasher.digest(&self.options.output.hash_digest))
2670 })
2671 .collect();
2672
2673 Ok(ChunkHashResult {
2674 hash: chunk_hash,
2675 content_hash: content_hashes,
2676 })
2677 }
2678
2679 #[instrument("Compilation:create_module_hashes", skip_all)]
2680 pub async fn create_module_hashes(&mut self, modules: IdentifierSet) -> Result<()> {
2681 let mg = self.get_module_graph();
2682 let chunk_graph = &self.chunk_graph;
2683 let chunk_by_ukey = &self.chunk_by_ukey;
2684
2685 let results = rspack_futures::scope::<_, Result<_>>(|token| {
2686 for module_identifier in modules {
2687 let s = unsafe { token.used((&*self, &mg, chunk_graph, chunk_by_ukey)) };
2688 s.spawn(
2689 move |(compilation, mg, chunk_graph, chunk_by_ukey)| async move {
2690 let mut hashes = RuntimeSpecMap::new();
2691 let module = mg
2692 .module_by_identifier(&module_identifier)
2693 .expect("should have module");
2694 for runtime in chunk_graph.get_module_runtimes_iter(module_identifier, chunk_by_ukey) {
2695 let hash = module.get_runtime_hash(compilation, Some(runtime)).await?;
2696 hashes.set(runtime.clone(), hash);
2697 }
2698 Ok((module_identifier, hashes))
2699 },
2700 );
2701 }
2702 })
2703 .await
2704 .into_iter()
2705 .map(|r| r.to_rspack_result())
2706 .collect::<Result<Vec<_>>>()?;
2707
2708 for result in results {
2709 let (module, hashes) = result?;
2710 if ChunkGraph::set_module_hashes(self, module, hashes)
2711 && let Some(mutations) = self.incremental.mutations_write()
2712 {
2713 mutations.add(Mutation::ModuleSetHashes { module });
2714 }
2715 }
2716 Ok(())
2717 }
2718
2719 pub fn add_runtime_module(
2720 &mut self,
2721 chunk_ukey: &ChunkUkey,
2722 mut module: Box<dyn RuntimeModule>,
2723 ) -> Result<()> {
2724 let chunk = self.chunk_by_ukey.expect_get(chunk_ukey);
2726 let runtime_module_identifier = ModuleIdentifier::from(format!(
2727 "{}/{}",
2728 get_runtime_key(chunk.runtime()),
2729 module.identifier()
2730 ));
2731 module.attach(*chunk_ukey);
2732
2733 self.chunk_graph.add_module(runtime_module_identifier);
2734 self.runtime_template.add_templates(module.template());
2735 self
2736 .chunk_graph
2737 .connect_chunk_and_module(*chunk_ukey, runtime_module_identifier);
2738 self
2739 .chunk_graph
2740 .connect_chunk_and_runtime_module(*chunk_ukey, runtime_module_identifier);
2741
2742 self
2743 .runtime_modules
2744 .insert(runtime_module_identifier, module);
2745
2746 Ok(())
2747 }
2748
2749 pub fn get_hash(&self) -> Option<&str> {
2750 self
2751 .hash
2752 .as_ref()
2753 .map(|hash| hash.rendered(self.options.output.hash_digest_length))
2754 }
2755
2756 pub async fn get_path<'b, 'a: 'b>(
2757 &'a self,
2758 filename: &Filename,
2759 mut data: PathData<'b>,
2760 ) -> Result<String> {
2761 if data.hash.is_none() {
2762 data.hash = self.get_hash();
2763 }
2764 filename.render(data, None).await
2765 }
2766
2767 pub async fn get_path_with_info<'b, 'a: 'b>(
2768 &'a self,
2769 filename: &Filename,
2770 mut data: PathData<'b>,
2771 info: &mut AssetInfo,
2772 ) -> Result<String> {
2773 if data.hash.is_none() {
2774 data.hash = self.get_hash();
2775 }
2776 let path = filename.render(data, Some(info)).await?;
2777 Ok(path)
2778 }
2779
2780 pub async fn get_asset_path(&self, filename: &Filename, data: PathData<'_>) -> Result<String> {
2781 filename.render(data, None).await
2782 }
2783
2784 pub async fn get_asset_path_with_info(
2785 &self,
2786 filename: &Filename,
2787 data: PathData<'_>,
2788 ) -> Result<(String, AssetInfo)> {
2789 let mut info = AssetInfo::default();
2790 let path = filename.render(data, Some(&mut info)).await?;
2791 Ok((path, info))
2792 }
2793
2794 pub fn get_logger(&self, name: impl Into<String>) -> CompilationLogger {
2795 CompilationLogger::new(name.into(), self.logging.clone())
2796 }
2797
2798 pub fn set_dependency_factory(
2799 &mut self,
2800 dependency_type: DependencyType,
2801 module_factory: Arc<dyn ModuleFactory>,
2802 ) {
2803 self
2804 .dependency_factories
2805 .insert(dependency_type, module_factory);
2806 }
2807
2808 pub fn get_dependency_factory(&self, dependency: &BoxDependency) -> Arc<dyn ModuleFactory> {
2809 let dependency_type = dependency.dependency_type();
2810 self
2811 .dependency_factories
2812 .get(dependency_type)
2813 .unwrap_or_else(|| {
2814 panic!(
2815 "No module factory available for dependency type: {}, resourceIdentifier: {:?}",
2816 dependency_type,
2817 dependency.resource_identifier()
2818 )
2819 })
2820 .clone()
2821 }
2822
2823 pub fn set_dependency_template(
2824 &mut self,
2825 template_type: DependencyTemplateType,
2826 template: Arc<dyn DependencyTemplate>,
2827 ) {
2828 self.dependency_templates.insert(template_type, template);
2829 }
2830
2831 pub fn get_dependency_template(
2832 &self,
2833 dep: &dyn DependencyCodeGeneration,
2834 ) -> Option<Arc<dyn DependencyTemplate>> {
2835 dep
2836 .dependency_template()
2837 .and_then(|template_type| self.dependency_templates.get(&template_type).cloned())
2838 }
2839}
2840
2841pub type CompilationAssets = HashMap<String, CompilationAsset>;
2842
2843#[cacheable]
2844#[derive(Debug, Clone)]
2845pub struct CompilationAsset {
2846 #[cacheable(with=AsOption<AsPreset>)]
2847 pub source: Option<BoxSource>,
2848 pub info: BindingCell<AssetInfo>,
2849}
2850
2851impl From<BoxSource> for CompilationAsset {
2852 fn from(value: BoxSource) -> Self {
2853 Self::new(Some(value), Default::default())
2854 }
2855}
2856
2857impl CompilationAsset {
2858 pub fn new(source: Option<BoxSource>, info: AssetInfo) -> Self {
2859 Self {
2860 source,
2861 info: BindingCell::from(info),
2862 }
2863 }
2864
2865 pub fn get_source(&self) -> Option<&BoxSource> {
2866 self.source.as_ref()
2867 }
2868
2869 pub fn get_source_mut(&mut self) -> Option<&mut BoxSource> {
2870 self.source.as_mut()
2871 }
2872
2873 pub fn set_source(&mut self, source: Option<BoxSource>) {
2874 self.source = source;
2875 }
2876
2877 pub fn get_info(&self) -> &AssetInfo {
2878 &self.info
2879 }
2880
2881 pub fn get_info_mut(&mut self) -> &mut AssetInfo {
2882 &mut self.info
2883 }
2884
2885 pub fn set_info(&mut self, info: AssetInfo) {
2886 self.info = BindingCell::from(info);
2887 }
2888}
2889
2890#[cacheable]
2891#[derive(Debug, Default, Clone)]
2892pub struct AssetInfo {
2893 pub immutable: Option<bool>,
2895 pub minimized: Option<bool>,
2897 pub full_hash: HashSet<String>,
2899 pub chunk_hash: HashSet<String>,
2901 pub content_hash: HashSet<String>,
2905 pub source_filename: Option<String>,
2907 pub copied: Option<bool>,
2909 pub development: Option<bool>,
2913 pub hot_module_replacement: Option<bool>,
2915 pub javascript_module: Option<bool>,
2917 pub related: AssetInfoRelated,
2919 pub version: String,
2922 pub css_unused_idents: Option<HashSet<String>>,
2924 pub is_over_size_limit: Option<bool>,
2926
2927 #[cacheable(with=AsPreset)]
2931 pub extras: serde_json::Map<String, serde_json::Value>,
2932}
2933
2934impl AssetInfo {
2935 pub fn with_minimized(mut self, v: Option<bool>) -> Self {
2936 self.minimized = v;
2937 self
2938 }
2939
2940 pub fn with_development(mut self, v: Option<bool>) -> Self {
2941 self.development = v;
2942 self
2943 }
2944
2945 pub fn with_hot_module_replacement(mut self, v: Option<bool>) -> Self {
2946 self.hot_module_replacement = v;
2947 self
2948 }
2949
2950 pub fn with_related(mut self, v: AssetInfoRelated) -> Self {
2951 self.related = v;
2952 self
2953 }
2954
2955 pub fn with_content_hashes(mut self, v: HashSet<String>) -> Self {
2956 self.content_hash = v;
2957 self
2958 }
2959
2960 pub fn with_version(mut self, v: String) -> Self {
2961 self.version = v;
2962 self
2963 }
2964
2965 pub fn set_full_hash(&mut self, v: String) {
2966 self.full_hash.insert(v);
2967 }
2968
2969 pub fn set_content_hash(&mut self, v: String) {
2970 self.content_hash.insert(v);
2971 }
2972
2973 pub fn set_chunk_hash(&mut self, v: String) {
2974 self.chunk_hash.insert(v);
2975 }
2976
2977 pub fn set_immutable(&mut self, v: Option<bool>) {
2978 self.immutable = v;
2979 }
2980
2981 pub fn set_source_filename(&mut self, v: String) {
2982 self.source_filename = Some(v);
2983 }
2984
2985 pub fn set_javascript_module(&mut self, v: bool) {
2986 self.javascript_module = Some(v);
2987 }
2988
2989 pub fn set_css_unused_idents(&mut self, v: HashSet<String>) {
2990 self.css_unused_idents = Some(v);
2991 }
2992
2993 pub fn set_is_over_size_limit(&mut self, v: bool) {
2994 self.is_over_size_limit = Some(v);
2995 }
2996 pub fn merge_another_asset(&mut self, another: AssetInfo) {
3002 self.minimized = another.minimized;
3004
3005 self.source_filename = another.source_filename.or(self.source_filename.take());
3006 self.version = another.version;
3007 self.related.merge_another(another.related);
3008
3009 self.chunk_hash.extend(another.chunk_hash);
3011 self.content_hash.extend(another.content_hash);
3012 self.extras.extend(another.extras);
3013 self.javascript_module = another.javascript_module.or(self.javascript_module.take());
3018 self.immutable = another.immutable.or(self.immutable);
3019 self.development = another.development.or(self.development);
3020 self.hot_module_replacement = another
3021 .hot_module_replacement
3022 .or(self.hot_module_replacement);
3023 self.is_over_size_limit = another.is_over_size_limit.or(self.is_over_size_limit);
3024 }
3025}
3026
3027#[cacheable]
3028#[derive(Debug, Default, Clone)]
3029pub struct AssetInfoRelated {
3030 pub source_map: Option<String>,
3031}
3032
3033impl AssetInfoRelated {
3034 pub fn merge_another(&mut self, another: AssetInfoRelated) {
3035 if let Some(source_map) = another.source_map {
3036 self.source_map = Some(source_map);
3037 }
3038 }
3039}
3040
3041pub fn assign_depths<'a>(
3044 assign_map: &mut IdentifierMap<usize>,
3045 modules: impl Iterator<Item = &'a ModuleIdentifier>,
3046 outgoings: &IdentifierMap<Vec<ModuleIdentifier>>,
3047) {
3048 let mut q = VecDeque::new();
3050 for item in modules {
3051 q.push_back((*item, 0));
3052 }
3053 while let Some((id, depth)) = q.pop_front() {
3054 match assign_map.entry(id) {
3055 hash_map::Entry::Occupied(_) => {
3056 continue;
3057 }
3058 hash_map::Entry::Vacant(vac) => {
3059 vac.insert(depth);
3060 }
3061 };
3062 for con in outgoings.get(&id).expect("should have outgoings").iter() {
3063 q.push_back((*con, depth + 1));
3064 }
3065 }
3066}
3067
3068pub fn assign_depth(
3069 assign_map: &mut IdentifierMap<usize>,
3070 mg: &ModuleGraph,
3071 module_id: ModuleIdentifier,
3072) {
3073 let mut q = VecDeque::new();
3075 q.push_back(module_id);
3076 let mut depth;
3077 assign_map.insert(module_id, 0);
3078 let process_module = |m: ModuleIdentifier,
3079 depth: usize,
3080 q: &mut VecDeque<ModuleIdentifier>,
3081 assign_map: &mut IdentifierMap<usize>| {
3082 if !set_depth_if_lower(m, depth, assign_map) {
3083 return;
3084 }
3085 q.push_back(m);
3086 };
3087 while let Some(item) = q.pop_front() {
3088 depth = assign_map.get(&item).expect("should have depth") + 1;
3089
3090 for con in mg.get_outgoing_connections(&item) {
3091 process_module(*con.module_identifier(), depth, &mut q, assign_map);
3092 }
3093 }
3094}
3095
3096pub fn set_depth_if_lower(
3097 module_id: ModuleIdentifier,
3098 depth: usize,
3099 assign_map: &mut IdentifierMap<usize>,
3100) -> bool {
3101 let Some(&cur_depth) = assign_map.get(&module_id) else {
3102 assign_map.insert(module_id, depth);
3103 return true;
3104 };
3105 if cur_depth > depth {
3106 assign_map.insert(module_id, depth);
3107 return true;
3108 }
3109 false
3110}
3111
3112#[derive(Debug, Clone)]
3113pub struct RenderManifestEntry {
3114 pub source: BoxSource,
3115 pub filename: String,
3116 pub has_filename: bool, pub info: AssetInfo,
3118 pub auxiliary: bool,
3119}
3120
3121pub struct ChunkHashResult {
3122 pub hash: RspackHashDigest,
3123 pub content_hash: ChunkContentHash,
3124}