rspack_core/compiler/
compilation.rs

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
154/// Use macro to prevent cargo shear from failing and reporting errors
155/// due to the inability to parse the async closure syntax
156/// https://github.com/Boshen/cargo-shear/issues/143
157macro_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        // runtime_requirements: rt_requirements of last time
176        // runtime_requirements_mut: changed rt_requirements
177        // requirements: all rt_requirements
178        call_hook(
179          self,
180          requirements,
181          &runtime_requirements,
182          &mut runtime_requirements_mut,
183        )
184        .await?;
185
186        // check if we have changes to runtime_requirements
187        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  /// get_compilation_hooks(compilation.id)
203  id: CompilationId,
204  compiler_id: CompilerId,
205  // Mark compilation status, because the hash of `[hash].hot-update.js/json` is previous compilation hash.
206  // Status A(hash: A) -> Status B(hash: B) will generate `A.hot-update.js`
207  // Status A(hash: A) -> Status C(hash: C) will generate `A.hot-update.js`
208  // The status is different, should generate different hash for `.hot-update.js`
209  // So use compilation hash update `hot_index` to fix it.
210  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  // artifact for infer_async_modules_plugin
240  pub async_modules_artifact: AsyncModulesArtifact,
241  // artifact for collect_dependencies_diagnostics
242  pub dependencies_diagnostics_artifact: DependenciesDiagnosticsArtifact,
243  // artifact for side_effects_flag_plugin
244  pub side_effects_optimize_artifact: SideEffectsOptimizeArtifact,
245  // artifact for module_ids
246  pub module_ids_artifact: ModuleIdsArtifact,
247  // artifact for chunk_ids
248  pub chunk_ids_artifact: ChunkIdsArtifact,
249  // artifact for code_generation
250  pub code_generation_results: BindingCell<CodeGenerationResults>,
251  // artifact for create_module_hashes
252  pub cgm_hash_artifact: CgmHashArtifact,
253  // artifact for process_modules_runtime_requirements
254  pub cgm_runtime_requirements_artifact: CgmRuntimeRequirementsArtifact,
255  // artifact for process_chunks_runtime_requirements
256  pub cgc_runtime_requirements_artifact: CgcRuntimeRequirementsArtifact,
257  // artifact for create_hash
258  pub chunk_hashes_artifact: ChunkHashesArtifact,
259  // artifact for create_chunk_assets
260  pub chunk_render_artifact: ChunkRenderArtifact,
261  // artifact for caching get_mode
262  pub module_graph_cache_artifact: ModuleGraphCacheArtifact,
263  // artifact for caching module static info
264  pub module_static_cache_artifact: ModuleStaticCacheArtifact,
265  // artifact for chunk render cache
266  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  // TODO move to MakeArtifact
287  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  /// A flag indicating whether the current compilation is being rebuilt.
299  ///
300  /// Rebuild will include previous compilation data, so persistent cache will not recovery anything
301  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, // FIXME: this should be removed in future
398        },
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  // FIXME: find a better way to do this.
464  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  // TODO move out from compilation
582  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    // Recheck entry and clean useless entry
706    // This should before finish_modules hook is called, ensure providedExports effects on new added modules
707    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            // TODO: source file name
799            ""
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      // Update related in all other assets
833      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  // Batch version of rename_asset with parallel optimization.
858  // Multiple calls to rename_asset would cause performance degradation due to
859  // repeated full traversals of chunk_by_ukey. This method uses parallel iteration
860  // over chunk_by_ukey to reduce traversal frequency and improve performance.
861  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        // Update related in all other assets
881        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  /// Get sorted errors based on the factors as follows in order:
929  /// - module identifier
930  /// - error offset
931  ///   Rspack assumes for each offset, there is only one error.
932  ///   However, when it comes to the case that there are multiple errors with the same offset,
933  ///   the order of these errors will not be guaranteed.
934  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  /// Get sorted warnings based on the factors as follows in order:
955  /// - module identifier
956  /// - error offset
957  ///   Rspack assumes for each offset, there is only one error.
958  ///   However, when it comes to the case that there are multiple errors with the same offset,
959  ///   the order of these errors will not be guaranteed.
960  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    // run module_executor
1012    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    // https://github.com/webpack/webpack/blob/19ca74127f7668aaf60d59f4af8fcaee7924541a/lib/Compilation.js#L2462C21-L2462C25
1034    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        // SAFETY: await immediately and trust caller to poll future entirely
1126        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      // assets of executed modules are not in this compilation
1208      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        // SAFETY: await immediately and trust caller to poll future entirely
1289        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  // #[instrument(
1388  //   name = "Compilation:chunk_asset",
1389  //   skip(self, plugin_driver, chunk_ukey)
1390  // )]
1391  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        // some entry dependencies may not find module because of resolve failed
1414        // so use filter_map to ignore them
1415        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    // clean up the entry deps
1431    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    // sync assets to module graph from module_executor
1437    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    // make finished, make artifact should be readonly thereafter.
1444
1445    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    // finish_modules means the module graph (modules, connections, dependencies) are
1483    // frozen and start to optimize (provided exports, infer async, etc.) based on the
1484    // module graph, so any kind of change that affect these should be done before the
1485    // finish_modules
1486    plugin_driver
1487      .compilation_hooks
1488      .finish_modules
1489      .call(self)
1490      .await?;
1491
1492    logger.time_end(start);
1493
1494    // https://github.com/webpack/webpack/blob/19ca74127f7668aaf60d59f4af8fcaee7924541a/lib/Compilation.js#L2988
1495    self.module_graph_cache_artifact.freeze();
1496    // Collect dependencies diagnostics at here to make sure:
1497    // 1. after finish_modules: has provide exports info
1498    // 2. before optimize dependencies: side effects free module hasn't been skipped
1499    self.collect_dependencies_diagnostics();
1500    self.module_graph_cache_artifact.unfreeze();
1501
1502    // take make diagnostics
1503    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    // TODO move diagnostic collect to make
1514    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    // https://github.com/webpack/webpack/blob/main/lib/Compilation.js#L2809
1587    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    // https://github.com/webpack/webpack/blob/d15c73469fd71cf98734685225250148b68ddc79/lib/Compilation.js#L2812-L2814
1596
1597    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    // ModuleGraph is frozen for now on, we have a module graph that won't change
1610    // so now we can start to create a chunk graph based on the module graph
1611
1612    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    // ChunkGraph is frozen for now on, we have a chunk graph that won't change
1676    // so now we can start to generate assets based on the chunk graph
1677
1678    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      // check if module runtime changes
1723      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            // module has no runtime, skip
1736            continue;
1737          }
1738          if module_runtimes.len() == 1 {
1739            // single runtime
1740            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            // multiple runtimes
1754            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      // also cleanup for updated modules, for `insert(); insert();` the second insert() won't override the first insert() on code_generation_results
1811      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    // NOTE: webpack runs hooks.runtime_module in compilation.add_runtime_module
2218    // and overwrite the runtime_module.generate() to get new source in create_chunk_assets
2219    // this needs full runtime requirements, so run hooks.runtime_module after runtime_requirements_in_tree
2220    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    // Check if there are any chunks that depend on full hash, usually only runtime chunks are
2256    // possible to depend on full hash, but for library type commonjs/module, it's possible to
2257    // have non-runtime chunks depend on full hash, the library format plugin is using
2258    // dependent_full_hash hook to declare it.
2259    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    // create hash for runtime modules in other chunks
2341    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    // create hash for other chunks
2367    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    // collect references for runtime chunks
2387    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    // sort runtime chunks by its references
2413    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    // create warning for remaining circular references
2464    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    // create hash for runtime chunks and the runtime modules within them
2491    // The subsequent runtime chunks and runtime modules will depend on
2492    // the hash results of the previous runtime chunks and runtime modules.
2493    // Therefore, create hashes one by one in sequence.
2494    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    // create full hash
2537    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    // re-create runtime chunk hash that depend on full hash
2549    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    // add chunk runtime to prefix module identifier to avoid multiple entry runtime modules conflict
2725    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  /// if the asset can be long term cached forever (contains a hash)
2894  pub immutable: Option<bool>,
2895  /// whether the asset is minimized
2896  pub minimized: Option<bool>,
2897  /// the value(s) of the full hash used for this asset
2898  pub full_hash: HashSet<String>,
2899  /// the value(s) of the chunk hash used for this asset
2900  pub chunk_hash: HashSet<String>,
2901  /// the value(s) of the module hash used for this asset
2902  // pub module_hash:
2903  /// the value(s) of the content hash used for this asset
2904  pub content_hash: HashSet<String>,
2905  /// when asset was created from a source file (potentially transformed), the original filename relative to compilation context
2906  pub source_filename: Option<String>,
2907  /// when asset was created from a source file (potentially transformed), it should be flagged as copied
2908  pub copied: Option<bool>,
2909  /// size in bytes, only set after asset has been emitted
2910  // pub size: f64,
2911  /// when asset is only used for development and doesn't count towards user-facing assets
2912  pub development: Option<bool>,
2913  /// when asset ships data for updating an existing application (HMR)
2914  pub hot_module_replacement: Option<bool>,
2915  /// when asset is javascript and an ESM
2916  pub javascript_module: Option<bool>,
2917  /// related object to other assets, keyed by type of relation (only points from parent to child)
2918  pub related: AssetInfoRelated,
2919  /// the asset version, emit can be skipped when both filename and version are the same
2920  /// An empty string means no version, it will always emit
2921  pub version: String,
2922  /// unused local idents of the chunk
2923  pub css_unused_idents: Option<HashSet<String>>,
2924  /// whether this asset is over the size limit
2925  pub is_over_size_limit: Option<bool>,
2926
2927  /// Webpack: AssetInfo = KnownAssetInfo & Record<string, any>
2928  /// This is a hack to store the additional fields in the rust struct.
2929  /// Related: packages/rspack/src/Compilation.ts
2930  #[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  // another should have high priority than self
2997  // self = { immutable:true}
2998  // merge_another_asset({immutable: false})
2999  // self == { immutable: false}
3000  // align with https://github.com/webpack/webpack/blob/899f06934391baede59da3dcd35b5ef51c675dbe/lib/Compilation.js#L4554
3001  pub fn merge_another_asset(&mut self, another: AssetInfo) {
3002    // "another" first fields
3003    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    // merge vec fields
3010    self.chunk_hash.extend(another.chunk_hash);
3011    self.content_hash.extend(another.content_hash);
3012    self.extras.extend(another.extras);
3013    // self.full_hash.extend(another.full_hash.iter().cloned());
3014    // self.module_hash.extend(another.module_hash.iter().cloned());
3015
3016    // old first fields or truthy first fields
3017    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
3041/// level order, the impl is different from webpack, since we can't iterate a set and mutate it at
3042/// the same time.
3043pub fn assign_depths<'a>(
3044  assign_map: &mut IdentifierMap<usize>,
3045  modules: impl Iterator<Item = &'a ModuleIdentifier>,
3046  outgoings: &IdentifierMap<Vec<ModuleIdentifier>>,
3047) {
3048  // https://github.com/webpack/webpack/blob/1f99ad6367f2b8a6ef17cce0e058f7a67fb7db18/lib/Compilation.js#L3720
3049  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  // https://github.com/webpack/webpack/blob/1f99ad6367f2b8a6ef17cce0e058f7a67fb7db18/lib/Compilation.js#L3720
3074  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, /* webpack only asset has filename, js/css/wasm has filename template */
3117  pub info: AssetInfo,
3118  pub auxiliary: bool,
3119}
3120
3121pub struct ChunkHashResult {
3122  pub hash: RspackHashDigest,
3123  pub content_hash: ChunkContentHash,
3124}