rspack_core/compilation/
mod.rs

1mod after_seal;
2mod assign_runtime_ids;
3pub mod build_chunk_graph;
4pub mod build_module_graph;
5mod chunk_ids;
6mod code_generation;
7mod create_chunk_assets;
8mod create_hash;
9mod create_module_assets;
10mod create_module_hashes;
11mod finish_make;
12mod finish_module_graph;
13mod finish_modules;
14mod make;
15mod module_ids;
16mod optimize_chunk_modules;
17mod optimize_chunks;
18mod optimize_code_generation;
19mod optimize_dependencies;
20mod optimize_modules;
21mod optimize_tree;
22mod process_assets;
23mod run_passes;
24mod runtime_requirements;
25mod seal;
26use std::{
27  collections::{VecDeque, hash_map},
28  fmt::{self, Debug},
29  hash::{BuildHasherDefault, Hash},
30  mem,
31  sync::{
32    Arc,
33    atomic::{AtomicBool, AtomicU32, Ordering},
34  },
35};
36
37use atomic_refcell::AtomicRefCell;
38use dashmap::DashSet;
39use futures::future::BoxFuture;
40use indexmap::IndexMap;
41use itertools::Itertools;
42use rayon::prelude::*;
43use rspack_cacheable::{
44  cacheable,
45  with::{AsOption, AsPreset},
46};
47use rspack_collections::{
48  DatabaseItem, IdentifierDashMap, IdentifierMap, IdentifierSet, UkeyMap, UkeySet,
49};
50use rspack_error::{Diagnostic, Result, ToStringResultToRspackResultExt};
51use rspack_fs::{IntermediateFileSystem, ReadableFileSystem, WritableFileSystem};
52use rspack_hash::{RspackHash, RspackHashDigest};
53use rspack_hook::define_hook;
54use rspack_paths::{ArcPath, ArcPathIndexSet, ArcPathSet};
55use rspack_sources::BoxSource;
56use rspack_tasks::CompilerContext;
57#[cfg(allocative)]
58use rspack_util::allocative;
59use rspack_util::{itoa, tracing_preset::TRACING_BENCH_TARGET};
60use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet, FxHasher};
61use tracing::instrument;
62use ustr::Ustr;
63
64use crate::{
65  AsyncModulesArtifact, BindingCell, BoxDependency, BoxModule, BuildChunkGraphArtifact, CacheCount,
66  CacheOptions, CgcRuntimeRequirementsArtifact, CgmHashArtifact, CgmRuntimeRequirementsArtifact,
67  Chunk, ChunkByUkey, ChunkContentHash, ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey,
68  ChunkHashesArtifact, ChunkKind, ChunkNamedIdArtifact, ChunkRenderArtifact,
69  ChunkRenderCacheArtifact, ChunkRenderResult, ChunkUkey, CodeGenerateCacheArtifact,
70  CodeGenerationJob, CodeGenerationResult, CodeGenerationResults, CompilationLogger,
71  CompilationLogging, CompilerOptions, CompilerPlatform, ConcatenationScope,
72  DependenciesDiagnosticsArtifact, DependencyCodeGeneration, DependencyTemplate,
73  DependencyTemplateType, DependencyType, DerefOption, Entry, EntryData, EntryOptions,
74  EntryRuntime, Entrypoint, ExecuteModuleId, Filename, ImportPhase, ImportVarMap,
75  ImportedByDeferModulesArtifact, MemoryGCStorage, ModuleFactory, ModuleGraph,
76  ModuleGraphCacheArtifact, ModuleIdentifier, ModuleIdsArtifact, ModuleStaticCacheArtifact,
77  PathData, ProcessRuntimeRequirementsCacheArtifact, ResolverFactory, RuntimeGlobals,
78  RuntimeKeyMap, RuntimeMode, RuntimeModule, RuntimeSpec, RuntimeSpecMap, RuntimeTemplate,
79  SharedPluginDriver, SideEffectsOptimizeArtifact, SourceType, Stats, ValueCacheVersions,
80  compilation::build_module_graph::{
81    BuildModuleGraphArtifact, ModuleExecutor, UpdateParam, update_module_graph,
82  },
83  compiler::{CompilationRecords, CompilerId},
84  get_runtime_key,
85  incremental::{self, Incremental, IncrementalPasses, Mutation},
86  is_source_equal, to_identifier,
87};
88
89define_hook!(CompilationAddEntry: Series(compilation: &mut Compilation, entry_name: Option<&str>));
90define_hook!(CompilationBuildModule: Series(compiler_id: CompilerId, compilation_id: CompilationId, module: &mut BoxModule),tracing=false);
91define_hook!(CompilationRevokedModules: Series(compilation: &Compilation, revoked_modules: &IdentifierSet));
92define_hook!(CompilationStillValidModule: Series(compiler_id: CompilerId, compilation_id: CompilationId, module: &mut BoxModule));
93define_hook!(CompilationSucceedModule: Series(compiler_id: CompilerId, compilation_id: CompilationId, module: &mut BoxModule),tracing=false);
94define_hook!(CompilationExecuteModule:
95  Series(module: &ModuleIdentifier, runtime_modules: &IdentifierSet, code_generation_results: &BindingCell<CodeGenerationResults>, execute_module_id: &ExecuteModuleId));
96define_hook!(CompilationFinishModules: Series(compilation: &mut Compilation, async_modules_artifact: &mut AsyncModulesArtifact));
97define_hook!(CompilationSeal: Series(compilation: &mut Compilation));
98define_hook!(CompilationConcatenationScope: SeriesBail(compilation: &Compilation, curr_module: ModuleIdentifier) -> ConcatenationScope);
99define_hook!(CompilationOptimizeDependencies: SeriesBail(compilation: &Compilation, side_effects_optimize_artifact: &mut SideEffectsOptimizeArtifact,  build_module_graph_artifact: &mut BuildModuleGraphArtifact,
100 diagnostics: &mut Vec<Diagnostic>) -> bool);
101define_hook!(CompilationOptimizeModules: SeriesBail(compilation: &Compilation, diagnostics: &mut Vec<Diagnostic>) -> bool);
102define_hook!(CompilationAfterOptimizeModules: Series(compilation: &Compilation));
103define_hook!(CompilationOptimizeChunks: SeriesBail(compilation: &mut Compilation) -> bool);
104define_hook!(CompilationOptimizeTree: Series(compilation: &Compilation));
105define_hook!(CompilationOptimizeChunkModules: SeriesBail(compilation: &mut Compilation) -> bool);
106define_hook!(CompilationModuleIds: Series(compilation: &Compilation, module_ids: &mut ModuleIdsArtifact, diagnostics: &mut Vec<Diagnostic>));
107define_hook!(CompilationChunkIds: Series(compilation: &Compilation, chunk_by_ukey: &mut ChunkByUkey, named_chunk_ids_artifact: &mut ChunkNamedIdArtifact, diagnostics: &mut Vec<Diagnostic>));
108define_hook!(CompilationRuntimeModule: Series(compilation: &Compilation, module: &ModuleIdentifier, chunk: &ChunkUkey, runtime_modules: &mut IdentifierMap<Box<dyn RuntimeModule>>));
109define_hook!(CompilationAdditionalModuleRuntimeRequirements: Series(compilation: &Compilation, module_identifier: &ModuleIdentifier, runtime_requirements: &mut RuntimeGlobals),tracing=false);
110define_hook!(CompilationRuntimeRequirementInModule: SeriesBail(compilation: &Compilation, module_identifier: &ModuleIdentifier, all_runtime_requirements: &RuntimeGlobals, runtime_requirements: &RuntimeGlobals, runtime_requirements_mut: &mut RuntimeGlobals),tracing=false);
111define_hook!(CompilationAdditionalChunkRuntimeRequirements: Series(compilation: &mut Compilation, chunk_ukey: &ChunkUkey, runtime_requirements: &mut RuntimeGlobals));
112define_hook!(CompilationRuntimeRequirementInChunk: SeriesBail(compilation: &mut Compilation, chunk_ukey: &ChunkUkey, all_runtime_requirements: &RuntimeGlobals, runtime_requirements: &RuntimeGlobals, runtime_requirements_mut: &mut RuntimeGlobals));
113define_hook!(CompilationAdditionalTreeRuntimeRequirements: Series(compilation: &mut Compilation, chunk_ukey: &ChunkUkey, runtime_requirements: &mut RuntimeGlobals));
114define_hook!(CompilationRuntimeRequirementInTree: SeriesBail(compilation: &mut Compilation, chunk_ukey: &ChunkUkey, all_runtime_requirements: &RuntimeGlobals, runtime_requirements: &RuntimeGlobals, runtime_requirements_mut: &mut RuntimeGlobals));
115define_hook!(CompilationOptimizeCodeGeneration: Series(compilation: &Compilation, build_module_graph_artifact: &mut BuildModuleGraphArtifact, diagnostics: &mut Vec<Diagnostic>));
116define_hook!(CompilationAfterCodeGeneration: Series(compilation: &Compilation, diagnostics: &mut Vec<Diagnostic>));
117define_hook!(CompilationChunkHash: Series(compilation: &Compilation, chunk_ukey: &ChunkUkey, hasher: &mut RspackHash),tracing=false);
118define_hook!(CompilationContentHash: Series(compilation: &Compilation, chunk_ukey: &ChunkUkey, hashes: &mut HashMap<SourceType, RspackHash>));
119define_hook!(CompilationDependentFullHash: SeriesBail(compilation: &Compilation, chunk_ukey: &ChunkUkey) -> bool);
120define_hook!(CompilationRenderManifest: Series(compilation: &Compilation, chunk_ukey: &ChunkUkey, manifest: &mut Vec<RenderManifestEntry>, diagnostics: &mut Vec<Diagnostic>),tracing=false);
121define_hook!(CompilationChunkAsset: Series(compilation: &Compilation, chunk_ukey: &ChunkUkey, filename: &str));
122define_hook!(CompilationProcessAssets: Series(compilation: &mut Compilation));
123define_hook!(CompilationAfterProcessAssets: Series(compilation: &Compilation, diagnostics: &mut Vec<Diagnostic>));
124define_hook!(CompilationAfterSeal: Series(compilation: &Compilation),tracing=true);
125
126#[derive(Debug, Default)]
127pub struct CompilationHooks {
128  pub add_entry: CompilationAddEntryHook,
129  pub build_module: CompilationBuildModuleHook,
130  pub revoked_modules: CompilationRevokedModulesHook,
131  pub concatenation_scope: CompilationConcatenationScopeHook,
132  pub still_valid_module: CompilationStillValidModuleHook,
133  pub succeed_module: CompilationSucceedModuleHook,
134  pub execute_module: CompilationExecuteModuleHook,
135  pub finish_modules: CompilationFinishModulesHook,
136  pub seal: CompilationSealHook,
137  pub optimize_dependencies: CompilationOptimizeDependenciesHook,
138  pub optimize_modules: CompilationOptimizeModulesHook,
139  pub after_optimize_modules: CompilationAfterOptimizeModulesHook,
140  pub optimize_chunks: CompilationOptimizeChunksHook,
141  pub optimize_tree: CompilationOptimizeTreeHook,
142  pub optimize_chunk_modules: CompilationOptimizeChunkModulesHook,
143  pub module_ids: CompilationModuleIdsHook,
144  pub chunk_ids: CompilationChunkIdsHook,
145  pub runtime_module: CompilationRuntimeModuleHook,
146  pub additional_module_runtime_requirements: CompilationAdditionalModuleRuntimeRequirementsHook,
147  pub runtime_requirement_in_module: CompilationRuntimeRequirementInModuleHook,
148  pub additional_chunk_runtime_requirements: CompilationAdditionalChunkRuntimeRequirementsHook,
149  pub runtime_requirement_in_chunk: CompilationRuntimeRequirementInChunkHook,
150  pub additional_tree_runtime_requirements: CompilationAdditionalTreeRuntimeRequirementsHook,
151  pub runtime_requirement_in_tree: CompilationRuntimeRequirementInTreeHook,
152  pub optimize_code_generation: CompilationOptimizeCodeGenerationHook,
153  pub after_code_generation: CompilationAfterCodeGenerationHook,
154  pub chunk_hash: CompilationChunkHashHook,
155  pub content_hash: CompilationContentHashHook,
156  pub dependent_full_hash: CompilationDependentFullHashHook,
157  pub render_manifest: CompilationRenderManifestHook,
158  pub chunk_asset: CompilationChunkAssetHook,
159  pub process_assets: CompilationProcessAssetsHook,
160  pub after_process_assets: CompilationAfterProcessAssetsHook,
161  pub after_seal: CompilationAfterSealHook,
162}
163
164#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
165#[cfg_attr(allocative, derive(allocative::Allocative))]
166pub struct CompilationId(pub u32);
167
168impl CompilationId {
169  pub fn new() -> Self {
170    Self(COMPILATION_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed))
171  }
172}
173
174impl Default for CompilationId {
175  fn default() -> Self {
176    Self::new()
177  }
178}
179
180static COMPILATION_ID: AtomicU32 = AtomicU32::new(0);
181
182/// Use macro to prevent cargo shear from failing and reporting errors
183/// due to the inability to parse the async closure syntax
184/// https://github.com/Boshen/cargo-shear/issues/143
185#[derive(Debug)]
186pub struct Compilation {
187  /// get_compilation_hooks(compilation.id)
188  id: CompilationId,
189  compiler_id: CompilerId,
190  // Mark compilation status, because the hash of `[hash].hot-update.js/json` is previous compilation hash.
191  // Status A(hash: A) -> Status B(hash: B) will generate `A.hot-update.js`
192  // Status A(hash: A) -> Status C(hash: C) will generate `A.hot-update.js`
193  // The status is different, should generate different hash for `.hot-update.js`
194  // So use compilation hash update `hot_index` to fix it.
195  pub hot_index: u32,
196  pub records: Option<CompilationRecords>,
197  pub options: Arc<CompilerOptions>,
198  pub platform: Arc<CompilerPlatform>,
199  pub entries: Entry,
200  pub global_entry: EntryData,
201  pub dependency_factories: HashMap<DependencyType, Arc<dyn ModuleFactory>>,
202  pub dependency_templates: HashMap<DependencyTemplateType, Arc<dyn DependencyTemplate>>,
203  pub runtime_modules: IdentifierMap<Box<dyn RuntimeModule>>,
204  pub runtime_modules_hash: IdentifierMap<RspackHashDigest>,
205  pub runtime_modules_code_generation_source: IdentifierMap<BoxSource>,
206  pub chunk_graph: ChunkGraph,
207  pub chunk_by_ukey: ChunkByUkey,
208  pub chunk_group_by_ukey: ChunkGroupByUkey,
209  pub entrypoints: IndexMap<String, ChunkGroupUkey>,
210  pub async_entrypoints: Vec<ChunkGroupUkey>,
211  assets: CompilationAssets,
212  assets_related_in: HashMap<String, HashSet<String>>,
213  pub emitted_assets: DashSet<String, BuildHasherDefault<FxHasher>>,
214  diagnostics: Vec<Diagnostic>,
215  logging: CompilationLogging,
216  pub plugin_driver: SharedPluginDriver,
217  pub buildtime_plugin_driver: SharedPluginDriver,
218  pub resolver_factory: Arc<ResolverFactory>,
219  pub loader_resolver_factory: Arc<ResolverFactory>,
220  pub named_chunks: HashMap<String, ChunkUkey>,
221  pub named_chunk_groups: HashMap<String, ChunkGroupUkey>,
222  pub runtime_template: RuntimeTemplate,
223
224  // artifact for infer_async_modules_plugin
225  pub async_modules_artifact: Arc<AtomicRefCell<AsyncModulesArtifact>>,
226  // artifact for collect_dependencies_diagnostics
227  pub dependencies_diagnostics_artifact: Arc<AtomicRefCell<DependenciesDiagnosticsArtifact>>,
228  // artifact for side_effects_flag_plugin
229  pub side_effects_optimize_artifact: DerefOption<SideEffectsOptimizeArtifact>,
230  // artifact for module_ids
231  pub module_ids_artifact: ModuleIdsArtifact,
232  // artifact for named_chunk_ids
233  pub named_chunk_ids_artifact: ChunkNamedIdArtifact,
234  // artifact for code_generation
235  pub code_generation_results: BindingCell<CodeGenerationResults>,
236  // artifact for create_module_hashes
237  pub cgm_hash_artifact: CgmHashArtifact,
238  // artifact for process_modules_runtime_requirements
239  pub cgm_runtime_requirements_artifact: CgmRuntimeRequirementsArtifact,
240  // artifact for process_chunks_runtime_requirements
241  pub cgc_runtime_requirements_artifact: CgcRuntimeRequirementsArtifact,
242  // artifact for create_hash
243  pub chunk_hashes_artifact: ChunkHashesArtifact,
244  // artifact for create_chunk_assets
245  pub chunk_render_artifact: ChunkRenderArtifact,
246  // artifact for caching get_mode
247  pub module_graph_cache_artifact: ModuleGraphCacheArtifact,
248  // artifact for caching module static info
249  pub module_static_cache_artifact: ModuleStaticCacheArtifact,
250  // artifact for chunk render cache
251  pub chunk_render_cache_artifact: ChunkRenderCacheArtifact,
252  // artifact for code generate cache
253  pub code_generate_cache_artifact: CodeGenerateCacheArtifact,
254  // artifact for process runtime requirements cache
255  pub process_runtime_requirements_cache_artifact: ProcessRuntimeRequirementsCacheArtifact,
256  pub imported_by_defer_modules_artifact: ImportedByDeferModulesArtifact,
257
258  pub code_generated_modules: IdentifierSet,
259  pub build_time_executed_modules: IdentifierSet,
260  pub build_chunk_graph_artifact: BuildChunkGraphArtifact,
261  pub incremental: Incremental,
262
263  pub hash: Option<RspackHashDigest>,
264
265  pub file_dependencies: ArcPathIndexSet,
266  pub context_dependencies: ArcPathIndexSet,
267  pub missing_dependencies: ArcPathIndexSet,
268  pub build_dependencies: ArcPathIndexSet,
269
270  pub value_cache_versions: ValueCacheVersions,
271
272  import_var_map: IdentifierDashMap<RuntimeKeyMap<ImportVarMap>>,
273
274  // TODO move to MakeArtifact
275  pub module_executor: Option<ModuleExecutor>,
276  in_finish_make: AtomicBool,
277
278  pub modified_files: ArcPathSet,
279  pub removed_files: ArcPathSet,
280  pub build_module_graph_artifact: DerefOption<BuildModuleGraphArtifact>,
281  pub input_filesystem: Arc<dyn ReadableFileSystem>,
282
283  pub intermediate_filesystem: Arc<dyn IntermediateFileSystem>,
284  pub output_filesystem: Arc<dyn WritableFileSystem>,
285
286  /// A flag indicating whether the current compilation is being rebuilt.
287  ///
288  /// Rebuild will include previous compilation data, so persistent cache will not recovery anything
289  pub is_rebuild: bool,
290  pub compiler_context: Arc<CompilerContext>,
291}
292
293impl Compilation {
294  pub const OPTIMIZE_CHUNKS_STAGE_BASIC: i32 = -10;
295  pub const OPTIMIZE_CHUNKS_STAGE_DEFAULT: i32 = 0;
296  pub const OPTIMIZE_CHUNKS_STAGE_ADVANCED: i32 = 10;
297
298  pub const PROCESS_ASSETS_STAGE_ADDITIONAL: i32 = -2000;
299  pub const PROCESS_ASSETS_STAGE_PRE_PROCESS: i32 = -1000;
300  pub const PROCESS_ASSETS_STAGE_DERIVED: i32 = -200;
301  pub const PROCESS_ASSETS_STAGE_ADDITIONS: i32 = -100;
302  pub const PROCESS_ASSETS_STAGE_OPTIMIZE: i32 = 100;
303  pub const PROCESS_ASSETS_STAGE_OPTIMIZE_COUNT: i32 = 200;
304  pub const PROCESS_ASSETS_STAGE_OPTIMIZE_COMPATIBILITY: i32 = 300;
305  pub const PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE: i32 = 400;
306  pub const PROCESS_ASSETS_STAGE_DEV_TOOLING: i32 = 500;
307  pub const PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE: i32 = 700;
308  pub const PROCESS_ASSETS_STAGE_SUMMARIZE: i32 = 1000;
309  pub const PROCESS_ASSETS_STAGE_OPTIMIZE_HASH: i32 = 2500;
310  pub const PROCESS_ASSETS_STAGE_AFTER_OPTIMIZE_HASH: i32 = 2600;
311  pub const PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER: i32 = 3000;
312  pub const PROCESS_ASSETS_STAGE_ANALYSE: i32 = 4000;
313  pub const PROCESS_ASSETS_STAGE_REPORT: i32 = 5000;
314
315  #[allow(clippy::too_many_arguments)]
316  pub fn new(
317    compiler_id: CompilerId,
318    options: Arc<CompilerOptions>,
319    platform: Arc<CompilerPlatform>,
320    plugin_driver: SharedPluginDriver,
321    buildtime_plugin_driver: SharedPluginDriver,
322    resolver_factory: Arc<ResolverFactory>,
323    loader_resolver_factory: Arc<ResolverFactory>,
324    records: Option<CompilationRecords>,
325    incremental: Incremental,
326    module_executor: Option<ModuleExecutor>,
327    modified_files: ArcPathSet,
328    removed_files: ArcPathSet,
329    input_filesystem: Arc<dyn ReadableFileSystem>,
330    intermediate_filesystem: Arc<dyn IntermediateFileSystem>,
331    output_filesystem: Arc<dyn WritableFileSystem>,
332    is_rebuild: bool,
333    compiler_context: Arc<CompilerContext>,
334  ) -> Self {
335    Self {
336      id: CompilationId::new(),
337      compiler_id,
338      hot_index: 0,
339      runtime_template: RuntimeTemplate::new(options.clone()),
340      records,
341      options: options.clone(),
342      platform,
343      dependency_factories: Default::default(),
344      dependency_templates: Default::default(),
345      runtime_modules: Default::default(),
346      runtime_modules_hash: Default::default(),
347      runtime_modules_code_generation_source: Default::default(),
348      chunk_by_ukey: Default::default(),
349      chunk_group_by_ukey: Default::default(),
350      entries: Default::default(),
351      global_entry: Default::default(),
352      chunk_graph: Default::default(),
353      entrypoints: Default::default(),
354      async_entrypoints: Default::default(),
355      assets: Default::default(),
356      assets_related_in: Default::default(),
357      emitted_assets: Default::default(),
358      diagnostics: Default::default(),
359      logging: Default::default(),
360      plugin_driver,
361      buildtime_plugin_driver,
362      resolver_factory,
363      loader_resolver_factory,
364      named_chunks: Default::default(),
365      named_chunk_groups: Default::default(),
366
367      async_modules_artifact: Arc::new(AtomicRefCell::new(AsyncModulesArtifact::default())),
368      imported_by_defer_modules_artifact: Default::default(),
369      dependencies_diagnostics_artifact: Arc::new(AtomicRefCell::new(
370        DependenciesDiagnosticsArtifact::default(),
371      )),
372      side_effects_optimize_artifact: DerefOption::new(Default::default()),
373      module_ids_artifact: Default::default(),
374      named_chunk_ids_artifact: Default::default(),
375      code_generation_results: Default::default(),
376      cgm_hash_artifact: Default::default(),
377      cgm_runtime_requirements_artifact: Default::default(),
378      cgc_runtime_requirements_artifact: Default::default(),
379      chunk_hashes_artifact: Default::default(),
380      chunk_render_artifact: Default::default(),
381      module_graph_cache_artifact: Default::default(),
382      module_static_cache_artifact: Default::default(),
383      code_generated_modules: Default::default(),
384      chunk_render_cache_artifact: ChunkRenderCacheArtifact::new(MemoryGCStorage::new(
385        match &options.cache {
386          CacheOptions::Memory { max_generations } => max_generations.unwrap_or(1),
387          CacheOptions::Disabled => 0, // FIXME: this should be removed in future
388        },
389      )),
390      code_generate_cache_artifact: CodeGenerateCacheArtifact::new(&options),
391      process_runtime_requirements_cache_artifact: ProcessRuntimeRequirementsCacheArtifact::new(
392        &options,
393      ),
394      build_time_executed_modules: Default::default(),
395      incremental,
396      build_chunk_graph_artifact: Default::default(),
397
398      hash: None,
399
400      file_dependencies: Default::default(),
401      context_dependencies: Default::default(),
402      missing_dependencies: Default::default(),
403      build_dependencies: Default::default(),
404
405      value_cache_versions: ValueCacheVersions::default(),
406
407      import_var_map: IdentifierDashMap::default(),
408
409      module_executor,
410      in_finish_make: AtomicBool::new(false),
411
412      build_module_graph_artifact: DerefOption::new(BuildModuleGraphArtifact::default()),
413      modified_files,
414      removed_files,
415      input_filesystem,
416
417      intermediate_filesystem,
418      output_filesystem,
419      is_rebuild,
420      compiler_context,
421    }
422  }
423
424  pub fn id(&self) -> CompilationId {
425    self.id
426  }
427
428  pub fn compiler_id(&self) -> CompilerId {
429    self.compiler_id
430  }
431
432  pub fn recover_module_graph_to_new_compilation(&mut self, new_compilation: &mut Compilation) {
433    self
434      .build_module_graph_artifact
435      .get_module_graph_mut()
436      .reset();
437    std::mem::swap(
438      &mut self.build_module_graph_artifact,
439      &mut new_compilation.build_module_graph_artifact,
440    );
441  }
442  pub fn swap_build_module_graph_artifact(&mut self, make_artifact: &mut BuildModuleGraphArtifact) {
443    self.build_module_graph_artifact.swap(make_artifact);
444  }
445  pub fn get_module_graph(&self) -> &ModuleGraph {
446    self.build_module_graph_artifact.get_module_graph()
447  }
448
449  // it will return None during make phase since mg is incomplete
450  pub fn module_by_identifier(&self, identifier: &ModuleIdentifier) -> Option<&BoxModule> {
451    if self.build_module_graph_artifact.is_none() {
452      return None;
453    }
454    if let Some(module) = self.get_module_graph().module_by_identifier(identifier) {
455      return Some(module);
456    };
457
458    self
459      .build_module_graph_artifact
460      .get_module_graph()
461      .module_by_identifier(identifier)
462  }
463  pub fn get_module_graph_mut(&mut self) -> &mut ModuleGraph {
464    self.build_module_graph_artifact.get_module_graph_mut()
465  }
466
467  pub fn file_dependencies(
468    &self,
469  ) -> (
470    impl Iterator<Item = &ArcPath>,
471    impl Iterator<Item = &ArcPath>,
472    impl Iterator<Item = &ArcPath>,
473  ) {
474    let all_files = self
475      .build_module_graph_artifact
476      .file_dependencies
477      .files()
478      .chain(&self.file_dependencies);
479    let added_files = self
480      .build_module_graph_artifact
481      .file_dependencies
482      .added_files()
483      .chain(&self.file_dependencies);
484    let removed_files = self
485      .build_module_graph_artifact
486      .file_dependencies
487      .removed_files();
488    (all_files, added_files, removed_files)
489  }
490
491  pub fn context_dependencies(
492    &self,
493  ) -> (
494    impl Iterator<Item = &ArcPath>,
495    impl Iterator<Item = &ArcPath>,
496    impl Iterator<Item = &ArcPath>,
497  ) {
498    let all_files = self
499      .build_module_graph_artifact
500      .context_dependencies
501      .files()
502      .chain(&self.context_dependencies);
503    let added_files = self
504      .build_module_graph_artifact
505      .context_dependencies
506      .added_files()
507      .chain(&self.file_dependencies);
508    let removed_files = self
509      .build_module_graph_artifact
510      .context_dependencies
511      .removed_files();
512    (all_files, added_files, removed_files)
513  }
514
515  pub fn missing_dependencies(
516    &self,
517  ) -> (
518    impl Iterator<Item = &ArcPath>,
519    impl Iterator<Item = &ArcPath>,
520    impl Iterator<Item = &ArcPath>,
521  ) {
522    let all_files = self
523      .build_module_graph_artifact
524      .missing_dependencies
525      .files()
526      .chain(&self.missing_dependencies);
527    let added_files = self
528      .build_module_graph_artifact
529      .missing_dependencies
530      .added_files()
531      .chain(&self.file_dependencies);
532    let removed_files = self
533      .build_module_graph_artifact
534      .missing_dependencies
535      .removed_files();
536    (all_files, added_files, removed_files)
537  }
538
539  pub fn build_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      .build_module_graph_artifact
548      .build_dependencies
549      .files()
550      .chain(&self.build_dependencies);
551    let added_files = self
552      .build_module_graph_artifact
553      .build_dependencies
554      .added_files()
555      .chain(&self.file_dependencies);
556    let removed_files = self
557      .build_module_graph_artifact
558      .build_dependencies
559      .removed_files();
560    (all_files, added_files, removed_files)
561  }
562
563  // TODO move out from compilation
564  pub fn get_import_var(
565    &self,
566    module: ModuleIdentifier,
567    target_module: Option<&BoxModule>,
568    user_request: &str,
569    phase: ImportPhase,
570    runtime: Option<&RuntimeSpec>,
571  ) -> String {
572    let mut runtime_map = self.import_var_map.entry(module).or_default();
573    let import_var_map_of_module = runtime_map
574      .entry(
575        runtime
576          .map(|r| get_runtime_key(r).clone())
577          .unwrap_or_default(),
578      )
579      .or_default();
580    let len = import_var_map_of_module.len();
581    let is_deferred = phase.is_defer()
582      && !target_module
583        .map(|m| m.build_meta().has_top_level_await)
584        .unwrap_or_default();
585
586    match import_var_map_of_module.entry((target_module.map(|m| m.identifier()), is_deferred)) {
587      hash_map::Entry::Occupied(occ) => occ.get().clone(),
588      hash_map::Entry::Vacant(vac) => {
589        let mut b = itoa::Buffer::new();
590        let import_var = format!(
591          "{}__rspack_{}import_{}",
592          to_identifier(user_request),
593          match phase {
594            ImportPhase::Evaluation => "",
595            ImportPhase::Source => "",
596            ImportPhase::Defer => "DEFERRED_",
597          },
598          b.format(len)
599        );
600        vac.insert(import_var.clone());
601        import_var
602      }
603    }
604  }
605
606  pub async fn add_entry(&mut self, entry: BoxDependency, options: EntryOptions) -> Result<()> {
607    let entry_id = *entry.id();
608    let entry_name: Option<String> = options.name.clone();
609    self
610      .build_module_graph_artifact
611      .get_module_graph_mut()
612      .add_dependency(entry);
613    if let Some(name) = &entry_name {
614      if let Some(data) = self.entries.get_mut(name) {
615        data.dependencies.push(entry_id);
616        data.options.merge(options)?;
617      } else {
618        let data = EntryData {
619          dependencies: vec![entry_id],
620          include_dependencies: vec![],
621          options,
622        };
623        self.entries.insert(name.to_owned(), data);
624      }
625    } else {
626      self.global_entry.dependencies.push(entry_id);
627    }
628
629    self
630      .plugin_driver
631      .clone()
632      .compilation_hooks
633      .add_entry
634      .call(self, entry_name.as_deref())
635      .await?;
636
637    Ok(())
638  }
639
640  pub async fn add_entry_batch(&mut self, args: Vec<(BoxDependency, EntryOptions)>) -> Result<()> {
641    for (entry, options) in args {
642      self.add_entry(entry, options).await?;
643    }
644
645    let make_artifact = self.build_module_graph_artifact.take();
646    self.build_module_graph_artifact.replace(
647      update_module_graph(
648        self,
649        make_artifact,
650        vec![UpdateParam::BuildEntry(
651          self
652            .entries
653            .values()
654            .flat_map(|item| item.all_dependencies())
655            .chain(self.global_entry.all_dependencies())
656            .copied()
657            .collect(),
658        )],
659      )
660      .await?,
661    );
662    Ok(())
663  }
664
665  pub async fn add_include(&mut self, args: Vec<(BoxDependency, EntryOptions)>) -> Result<()> {
666    if !self.in_finish_make.load(Ordering::Acquire) {
667      return Err(rspack_error::Error::error(
668        "You can only call `add_include` during the finish make stage".into(),
669      ));
670    }
671
672    for (entry, options) in args {
673      let entry_id = *entry.id();
674      self
675        .build_module_graph_artifact
676        .get_module_graph_mut()
677        .add_dependency(entry);
678      if let Some(name) = options.name.clone() {
679        if let Some(data) = self.entries.get_mut(&name) {
680          data.include_dependencies.push(entry_id);
681        } else {
682          let data = EntryData {
683            dependencies: vec![],
684            include_dependencies: vec![entry_id],
685            options,
686          };
687          self.entries.insert(name, data);
688        }
689      } else {
690        self.global_entry.include_dependencies.push(entry_id);
691      }
692    }
693
694    // Recheck entry and clean useless entry
695    // This should before finish_modules hook is called, ensure providedExports effects on new added modules
696    let make_artifact = self.build_module_graph_artifact.take();
697    self.build_module_graph_artifact.replace(
698      update_module_graph(
699        self,
700        make_artifact,
701        vec![UpdateParam::BuildEntry(
702          self
703            .entries
704            .values()
705            .flat_map(|item| item.all_dependencies())
706            .chain(self.global_entry.all_dependencies())
707            .copied()
708            .collect(),
709        )],
710      )
711      .await?,
712    );
713    Ok(())
714  }
715
716  fn set_asset_info(
717    &mut self,
718    name: &str,
719    new_info: Option<&AssetInfo>,
720    old_info: Option<&AssetInfo>,
721  ) {
722    if let Some(old_info) = old_info
723      && let Some(source_map) = &old_info.related.source_map
724      && let Some(entry) = self.assets_related_in.get_mut(source_map)
725    {
726      entry.remove(name);
727    }
728    if let Some(new_info) = new_info
729      && let Some(source_map) = new_info.related.source_map.clone()
730    {
731      let entry = self.assets_related_in.entry(source_map).or_default();
732      entry.insert(name.to_string());
733    }
734  }
735
736  pub fn update_asset(
737    &mut self,
738    filename: &str,
739    updater: impl FnOnce(
740      BoxSource,
741      BindingCell<AssetInfo>,
742    ) -> Result<(BoxSource, BindingCell<AssetInfo>)>,
743  ) -> Result<()> {
744    let assets = &mut self.assets;
745
746    let (old_info, new_source, new_info) = match assets.remove(filename) {
747      Some(CompilationAsset {
748        source: Some(source),
749        info: old_info,
750      }) => {
751        let (new_source, new_info) = updater(source, old_info.clone())?;
752        (old_info, new_source, new_info)
753      }
754      _ => {
755        return Err(rspack_error::error!(
756          "Called Compilation.updateAsset for not existing filename {}",
757          filename
758        ));
759      }
760    };
761    self.set_asset_info(filename, Some(&new_info), Some(&old_info));
762    self.assets.insert(
763      filename.to_owned(),
764      CompilationAsset {
765        source: Some(new_source),
766        info: new_info,
767      },
768    );
769    Ok(())
770  }
771  #[instrument("Compilation:emit_asset",skip_all, fields(filename = filename))]
772  pub fn emit_asset(&mut self, filename: String, asset: CompilationAsset) {
773    if let Some(mut original) = self.assets.remove(&filename)
774      && let Some(original_source) = &original.source
775      && let Some(asset_source) = asset.get_source()
776    {
777      let is_source_equal = is_source_equal(original_source, asset_source);
778      if !is_source_equal {
779        tracing::error!(
780          "Emit Duplicate Filename({}), is_source_equal: {:?}",
781          filename,
782          is_source_equal
783        );
784        self.push_diagnostic(
785          rspack_error::error!(
786            "Conflict: Multiple assets emit different content to the same filename {}{}",
787            filename,
788            // TODO: source file name
789            ""
790          )
791          .into(),
792        );
793        self.set_asset_info(&filename, Some(asset.get_info()), None);
794        self.assets.insert(filename, asset);
795        return;
796      }
797      self.set_asset_info(&filename, Some(asset.get_info()), Some(original.get_info()));
798      original.info = asset.info;
799      self.assets.insert(filename, original);
800    } else {
801      self.set_asset_info(&filename, Some(asset.get_info()), None);
802      self.assets.insert(filename, asset);
803    }
804  }
805
806  pub fn delete_asset(&mut self, filename: &str) {
807    if let Some(asset) = self.assets.remove(filename) {
808      self.set_asset_info(filename, None, Some(asset.get_info()));
809
810      if let Some(source_map) = &asset.info.related.source_map {
811        self.delete_asset(source_map);
812      }
813      self.chunk_by_ukey.iter_mut().for_each(|(_, chunk)| {
814        chunk.remove_file(filename);
815        chunk.remove_auxiliary_file(filename);
816      });
817    }
818  }
819
820  pub fn rename_asset(&mut self, filename: &str, new_name: String) {
821    if let Some(asset) = self.assets.remove(filename) {
822      // Update related in all other assets
823      if let Some(related_in_info) = self.assets_related_in.get(filename) {
824        for name in related_in_info {
825          if let Some(asset) = self.assets.get_mut(name) {
826            asset.get_info_mut().related.source_map = Some(new_name.clone());
827          }
828        }
829      }
830      self.set_asset_info(filename, None, Some(asset.get_info()));
831      self.set_asset_info(&new_name, Some(asset.get_info()), None);
832
833      self.assets.insert(new_name.clone(), asset);
834
835      self.chunk_by_ukey.iter_mut().for_each(|(_, chunk)| {
836        if chunk.remove_file(filename) {
837          chunk.add_file(new_name.clone());
838        }
839
840        if chunk.remove_auxiliary_file(filename) {
841          chunk.add_auxiliary_file(new_name.clone());
842        }
843      });
844    }
845  }
846
847  // Batch version of rename_asset with parallel optimization.
848  // Multiple calls to rename_asset would cause performance degradation due to
849  // repeated full traversals of chunk_by_ukey. This method uses parallel iteration
850  // over chunk_by_ukey to reduce traversal frequency and improve performance.
851  pub fn par_rename_assets(&mut self, renames: Vec<(String, String)>) {
852    self
853      .chunk_by_ukey
854      .values_mut()
855      .par_bridge()
856      .for_each(|chunk| {
857        for (old_name, new_name) in renames.iter() {
858          if chunk.remove_file(old_name) {
859            chunk.add_file(new_name.clone());
860          }
861
862          if chunk.remove_auxiliary_file(old_name) {
863            chunk.add_auxiliary_file(new_name.clone());
864          }
865        }
866      });
867
868    for (old_name, new_name) in renames {
869      if let Some(asset) = self.assets.remove(&old_name) {
870        // Update related in all other assets
871        if let Some(related_in_info) = self.assets_related_in.get(&old_name) {
872          for related_in_name in related_in_info {
873            if let Some(asset) = self.assets.get_mut(related_in_name) {
874              asset.get_info_mut().related.source_map = Some(new_name.clone());
875            }
876          }
877        }
878        self.set_asset_info(&old_name, None, Some(asset.get_info()));
879        self.set_asset_info(&new_name, Some(asset.get_info()), None);
880
881        self.assets.insert(new_name, asset);
882      }
883    }
884  }
885
886  pub fn assets(&self) -> &CompilationAssets {
887    &self.assets
888  }
889
890  pub fn assets_mut(&mut self) -> &mut CompilationAssets {
891    &mut self.assets
892  }
893
894  pub fn entrypoints(&self) -> &IndexMap<String, ChunkGroupUkey> {
895    &self.entrypoints
896  }
897
898  pub fn push_diagnostic(&mut self, diagnostic: Diagnostic) {
899    self.diagnostics.push(diagnostic);
900  }
901
902  pub fn extend_diagnostics(&mut self, diagnostics: impl IntoIterator<Item = Diagnostic>) {
903    self.diagnostics.extend(diagnostics);
904  }
905
906  pub fn diagnostics(&self) -> &[Diagnostic] {
907    &self.diagnostics
908  }
909
910  pub fn diagnostics_mut(&mut self) -> &mut Vec<Diagnostic> {
911    &mut self.diagnostics
912  }
913
914  pub fn get_errors(&self) -> impl Iterator<Item = &Diagnostic> {
915    self.diagnostics.iter().filter(|d| d.is_error())
916  }
917
918  /// Get sorted errors based on the factors as follows in order:
919  /// - module identifier
920  /// - error offset
921  ///   Rspack assumes for each offset, there is only one error.
922  ///   However, when it comes to the case that there are multiple errors with the same offset,
923  ///   the order of these errors will not be guaranteed.
924  pub fn get_errors_sorted(&self) -> impl Iterator<Item = &Diagnostic> {
925    let get_offset = |d: &Diagnostic| {
926      d.labels
927        .as_ref()
928        .and_then(|l| l.first())
929        .map(|l| l.offset)
930        .unwrap_or_default()
931    };
932    self
933      .get_errors()
934      .sorted_by(|a, b| match a.module_identifier.cmp(&b.module_identifier) {
935        std::cmp::Ordering::Equal => get_offset(a).cmp(&get_offset(b)),
936        other => other,
937      })
938  }
939
940  pub fn get_warnings(&self) -> impl Iterator<Item = &Diagnostic> {
941    self.diagnostics.iter().filter(|d| d.is_warn())
942  }
943
944  /// Get sorted warnings based on the factors as follows in order:
945  /// - module identifier
946  /// - error offset
947  ///   Rspack assumes for each offset, there is only one error.
948  ///   However, when it comes to the case that there are multiple errors with the same offset,
949  ///   the order of these errors will not be guaranteed.
950  pub fn get_warnings_sorted(&self) -> impl Iterator<Item = &Diagnostic> {
951    let get_offset = |d: &Diagnostic| {
952      d.labels
953        .as_ref()
954        .and_then(|l| l.first())
955        .map(|l| l.offset)
956        .unwrap_or_default()
957    };
958    self
959      .get_warnings()
960      .sorted_by(|a, b| match a.module_identifier.cmp(&b.module_identifier) {
961        std::cmp::Ordering::Equal => get_offset(a).cmp(&get_offset(b)),
962        other => other,
963      })
964  }
965
966  pub fn get_logging(&self) -> &CompilationLogging {
967    &self.logging
968  }
969
970  pub fn get_stats(&self) -> Stats<'_> {
971    Stats::new(self)
972  }
973
974  pub fn add_named_chunk(
975    name: String,
976    chunk_by_ukey: &mut ChunkByUkey,
977    named_chunks: &mut HashMap<String, ChunkUkey>,
978  ) -> (ChunkUkey, bool) {
979    let existed_chunk_ukey = named_chunks.get(&name);
980    if let Some(chunk_ukey) = existed_chunk_ukey {
981      assert!(chunk_by_ukey.contains(chunk_ukey));
982      (*chunk_ukey, false)
983    } else {
984      let chunk = Chunk::new(Some(name.clone()), ChunkKind::Normal);
985      let ukey = chunk.ukey();
986      named_chunks.insert(name, ukey);
987      chunk_by_ukey.entry(ukey).or_insert_with(|| chunk);
988      (ukey, true)
989    }
990  }
991
992  pub fn add_chunk(chunk_by_ukey: &mut ChunkByUkey) -> ChunkUkey {
993    let chunk = Chunk::new(None, ChunkKind::Normal);
994    let ukey = chunk.ukey();
995    chunk_by_ukey.add(chunk);
996    ukey
997  }
998
999  pub async fn rebuild_module<T>(
1000    &mut self,
1001    module_identifiers: IdentifierSet,
1002    f: impl Fn(Vec<&BoxModule>) -> T,
1003  ) -> Result<T> {
1004    let artifact = self.build_module_graph_artifact.take();
1005
1006    // https://github.com/webpack/webpack/blob/19ca74127f7668aaf60d59f4af8fcaee7924541a/lib/Compilation.js#L2462C21-L2462C25
1007    self.module_graph_cache_artifact.unfreeze();
1008
1009    self.build_module_graph_artifact.replace(
1010      update_module_graph(
1011        self,
1012        artifact,
1013        vec![UpdateParam::ForceBuildModules(module_identifiers.clone())],
1014      )
1015      .await?,
1016    );
1017
1018    let module_graph = self.get_module_graph();
1019    Ok(f(module_identifiers
1020      .into_iter()
1021      .filter_map(|id| module_graph.module_by_identifier(&id))
1022      .collect::<Vec<_>>()))
1023  }
1024
1025  pub fn entry_modules(&self) -> IdentifierSet {
1026    let module_graph = self.get_module_graph();
1027    self
1028      .entries
1029      .values()
1030      .flat_map(|item| item.all_dependencies())
1031      .chain(self.global_entry.all_dependencies())
1032      .filter_map(|dep_id| {
1033        // some entry dependencies may not find module because of resolve failed
1034        // so use filter_map to ignore them
1035        module_graph
1036          .module_identifier_by_dependency_id(dep_id)
1037          .copied()
1038      })
1039      .collect()
1040  }
1041
1042  pub fn entrypoint_by_name(&self, name: &str) -> &Entrypoint {
1043    let ukey = self.entrypoints.get(name).expect("entrypoint not found");
1044    self.chunk_group_by_ukey.expect_get(ukey)
1045  }
1046
1047  pub fn entrypoint_by_name_mut(&mut self, name: &str) -> &mut Entrypoint {
1048    let ukey = self.entrypoints.get(name).expect("entrypoint not found");
1049    self.chunk_group_by_ukey.expect_get_mut(ukey)
1050  }
1051
1052  pub fn get_chunk_graph_entries(&self) -> impl Iterator<Item = ChunkUkey> + use<'_> {
1053    let entries = self.entrypoints.values().map(|entrypoint_ukey| {
1054      let entrypoint = self.chunk_group_by_ukey.expect_get(entrypoint_ukey);
1055      entrypoint.get_runtime_chunk(&self.chunk_group_by_ukey)
1056    });
1057    let async_entries = self.async_entrypoints.iter().map(|entrypoint_ukey| {
1058      let entrypoint = self.chunk_group_by_ukey.expect_get(entrypoint_ukey);
1059      entrypoint.get_runtime_chunk(&self.chunk_group_by_ukey)
1060    });
1061    entries.chain(async_entries)
1062  }
1063  pub fn add_runtime_module(
1064    &mut self,
1065    chunk_ukey: &ChunkUkey,
1066    mut module: Box<dyn RuntimeModule>,
1067  ) -> Result<()> {
1068    // add chunk runtime to prefix module identifier to avoid multiple entry runtime modules conflict
1069    let chunk = self.chunk_by_ukey.expect_get(chunk_ukey);
1070    let runtime_module_identifier = ModuleIdentifier::from(format!(
1071      "{}/{}",
1072      get_runtime_key(chunk.runtime()),
1073      module.identifier()
1074    ));
1075    module.attach(*chunk_ukey);
1076
1077    self.chunk_graph.add_module(runtime_module_identifier);
1078    self.runtime_template.add_templates(module.template());
1079    self
1080      .chunk_graph
1081      .connect_chunk_and_module(*chunk_ukey, runtime_module_identifier);
1082    self
1083      .chunk_graph
1084      .connect_chunk_and_runtime_module(*chunk_ukey, runtime_module_identifier);
1085
1086    self
1087      .runtime_modules
1088      .insert(runtime_module_identifier, module);
1089
1090    Ok(())
1091  }
1092
1093  pub fn get_hash(&self) -> Option<&str> {
1094    self
1095      .hash
1096      .as_ref()
1097      .map(|hash| hash.rendered(self.options.output.hash_digest_length))
1098  }
1099
1100  pub async fn get_path<'b, 'a: 'b>(
1101    &'a self,
1102    filename: &Filename,
1103    mut data: PathData<'b>,
1104  ) -> Result<String> {
1105    if data.hash.is_none() {
1106      data.hash = self.get_hash();
1107    }
1108    filename.render(data, None).await
1109  }
1110
1111  pub async fn get_path_with_info<'b, 'a: 'b>(
1112    &'a self,
1113    filename: &Filename,
1114    mut data: PathData<'b>,
1115    info: &mut AssetInfo,
1116  ) -> Result<String> {
1117    if data.hash.is_none() {
1118      data.hash = self.get_hash();
1119    }
1120    let path = filename.render(data, Some(info)).await?;
1121    Ok(path)
1122  }
1123
1124  pub async fn get_asset_path(&self, filename: &Filename, data: PathData<'_>) -> Result<String> {
1125    filename.render(data, None).await
1126  }
1127
1128  pub async fn get_asset_path_with_info(
1129    &self,
1130    filename: &Filename,
1131    data: PathData<'_>,
1132  ) -> Result<(String, AssetInfo)> {
1133    let mut info = AssetInfo::default();
1134    let path = filename.render(data, Some(&mut info)).await?;
1135    Ok((path, info))
1136  }
1137
1138  pub fn get_logger(&self, name: impl Into<String>) -> CompilationLogger {
1139    CompilationLogger::new(name.into(), self.logging.clone())
1140  }
1141
1142  pub fn set_dependency_factory(
1143    &mut self,
1144    dependency_type: DependencyType,
1145    module_factory: Arc<dyn ModuleFactory>,
1146  ) {
1147    self
1148      .dependency_factories
1149      .insert(dependency_type, module_factory);
1150  }
1151
1152  pub fn get_dependency_factory(&self, dependency: &BoxDependency) -> Arc<dyn ModuleFactory> {
1153    let dependency_type = dependency.dependency_type();
1154    self
1155      .dependency_factories
1156      .get(dependency_type)
1157      .unwrap_or_else(|| {
1158        panic!(
1159          "No module factory available for dependency type: {}, resourceIdentifier: {:?}",
1160          dependency_type,
1161          dependency.resource_identifier()
1162        )
1163      })
1164      .clone()
1165  }
1166
1167  pub fn set_dependency_template(
1168    &mut self,
1169    template_type: DependencyTemplateType,
1170    template: Arc<dyn DependencyTemplate>,
1171  ) {
1172    self.dependency_templates.insert(template_type, template);
1173  }
1174
1175  pub fn get_dependency_template(
1176    &self,
1177    dep: &dyn DependencyCodeGeneration,
1178  ) -> Option<Arc<dyn DependencyTemplate>> {
1179    dep
1180      .dependency_template()
1181      .and_then(|template_type| self.dependency_templates.get(&template_type).cloned())
1182  }
1183}
1184
1185pub type CompilationAssets = HashMap<String, CompilationAsset>;
1186
1187#[cacheable]
1188#[derive(Debug, Clone)]
1189pub struct CompilationAsset {
1190  #[cacheable(with=AsOption<AsPreset>)]
1191  pub source: Option<BoxSource>,
1192  pub info: BindingCell<AssetInfo>,
1193}
1194
1195impl From<BoxSource> for CompilationAsset {
1196  fn from(value: BoxSource) -> Self {
1197    Self::new(Some(value), Default::default())
1198  }
1199}
1200
1201impl CompilationAsset {
1202  pub fn new(source: Option<BoxSource>, info: AssetInfo) -> Self {
1203    Self {
1204      source,
1205      info: BindingCell::from(info),
1206    }
1207  }
1208
1209  pub fn get_source(&self) -> Option<&BoxSource> {
1210    self.source.as_ref()
1211  }
1212
1213  pub fn set_source(&mut self, source: Option<BoxSource>) {
1214    self.source = source;
1215  }
1216
1217  pub fn get_info(&self) -> &AssetInfo {
1218    &self.info
1219  }
1220
1221  pub fn get_info_mut(&mut self) -> &mut AssetInfo {
1222    &mut self.info
1223  }
1224
1225  pub fn set_info(&mut self, info: AssetInfo) {
1226    self.info = BindingCell::from(info);
1227  }
1228}
1229
1230#[cacheable]
1231#[derive(Debug, Default, Clone)]
1232pub struct AssetInfo {
1233  /// if the asset can be long term cached forever (contains a hash)
1234  pub immutable: Option<bool>,
1235  /// whether the asset is minimized
1236  pub minimized: Option<bool>,
1237  /// the value(s) of the full hash used for this asset
1238  pub full_hash: HashSet<String>,
1239  /// the value(s) of the chunk hash used for this asset
1240  pub chunk_hash: HashSet<String>,
1241  /// the value(s) of the module hash used for this asset
1242  // pub module_hash:
1243  /// the value(s) of the content hash used for this asset
1244  pub content_hash: HashSet<String>,
1245  /// when asset was created from a source file (potentially transformed), the original filename relative to compilation context
1246  pub source_filename: Option<String>,
1247  /// when asset was created from a source file (potentially transformed), it should be flagged as copied
1248  pub copied: Option<bool>,
1249  /// size in bytes, only set after asset has been emitted
1250  // pub size: f64,
1251  /// when asset is only used for development and doesn't count towards user-facing assets
1252  pub development: Option<bool>,
1253  /// when asset ships data for updating an existing application (HMR)
1254  pub hot_module_replacement: Option<bool>,
1255  /// when asset is javascript and an ESM
1256  pub javascript_module: Option<bool>,
1257  /// related object to other assets, keyed by type of relation (only points from parent to child)
1258  pub related: AssetInfoRelated,
1259  /// the asset version, emit can be skipped when both filename and version are the same
1260  /// An empty string means no version, it will always emit
1261  pub version: String,
1262  /// unused local idents of the chunk
1263  pub css_unused_idents: Option<HashSet<String>>,
1264  /// whether this asset is over the size limit
1265  pub is_over_size_limit: Option<bool>,
1266  /// the plugin that created the asset
1267  pub asset_type: ManifestAssetType,
1268
1269  /// Webpack: AssetInfo = KnownAssetInfo & Record<string, any>
1270  /// This is a hack to store the additional fields in the rust struct.
1271  /// Related: packages/rspack/src/Compilation.ts
1272  #[cacheable(with=AsPreset)]
1273  pub extras: serde_json::Map<String, serde_json::Value>,
1274}
1275
1276impl AssetInfo {
1277  pub fn with_development(mut self, v: Option<bool>) -> Self {
1278    self.development = v;
1279    self
1280  }
1281
1282  pub fn with_hot_module_replacement(mut self, v: Option<bool>) -> Self {
1283    self.hot_module_replacement = v;
1284    self
1285  }
1286
1287  pub fn with_content_hashes(mut self, v: HashSet<String>) -> Self {
1288    self.content_hash = v;
1289    self
1290  }
1291
1292  pub fn with_version(mut self, v: String) -> Self {
1293    self.version = v;
1294    self
1295  }
1296
1297  pub fn with_asset_type(mut self, v: ManifestAssetType) -> Self {
1298    self.asset_type = v;
1299    self
1300  }
1301
1302  pub fn set_full_hash(&mut self, v: String) {
1303    self.full_hash.insert(v);
1304  }
1305
1306  pub fn set_content_hash(&mut self, v: String) {
1307    self.content_hash.insert(v);
1308  }
1309
1310  pub fn set_chunk_hash(&mut self, v: String) {
1311    self.chunk_hash.insert(v);
1312  }
1313
1314  pub fn set_immutable(&mut self, v: Option<bool>) {
1315    self.immutable = v;
1316  }
1317
1318  pub fn set_source_filename(&mut self, v: String) {
1319    self.source_filename = Some(v);
1320  }
1321
1322  pub fn set_javascript_module(&mut self, v: bool) {
1323    self.javascript_module = Some(v);
1324  }
1325
1326  pub fn set_css_unused_idents(&mut self, v: HashSet<String>) {
1327    self.css_unused_idents = Some(v);
1328  }
1329
1330  pub fn set_is_over_size_limit(&mut self, v: bool) {
1331    self.is_over_size_limit = Some(v);
1332  }
1333  // another should have high priority than self
1334  // self = { immutable:true}
1335  // merge_another_asset({immutable: false})
1336  // self == { immutable: false}
1337  // align with https://github.com/webpack/webpack/blob/899f06934391baede59da3dcd35b5ef51c675dbe/lib/Compilation.js#L4554
1338  pub fn merge_another_asset(&mut self, another: AssetInfo) {
1339    // "another" first fields
1340    self.minimized = another.minimized;
1341
1342    self.source_filename = another.source_filename.or(self.source_filename.take());
1343    self.version = another.version;
1344    self.related.merge_another(another.related);
1345
1346    // merge vec fields
1347    self.chunk_hash.extend(another.chunk_hash);
1348    self.content_hash.extend(another.content_hash);
1349    self.extras.extend(another.extras);
1350    // self.full_hash.extend(another.full_hash.iter().cloned());
1351    // self.module_hash.extend(another.module_hash.iter().cloned());
1352
1353    // old first fields or truthy first fields
1354    self.javascript_module = another.javascript_module.or(self.javascript_module.take());
1355    self.immutable = another.immutable.or(self.immutable);
1356    self.development = another.development.or(self.development);
1357    self.hot_module_replacement = another
1358      .hot_module_replacement
1359      .or(self.hot_module_replacement);
1360    self.is_over_size_limit = another.is_over_size_limit.or(self.is_over_size_limit);
1361  }
1362}
1363
1364#[cacheable]
1365#[derive(Debug, Default, Clone)]
1366pub struct AssetInfoRelated {
1367  pub source_map: Option<String>,
1368}
1369
1370impl AssetInfoRelated {
1371  pub fn merge_another(&mut self, another: AssetInfoRelated) {
1372    if let Some(source_map) = another.source_map {
1373      self.source_map = Some(source_map);
1374    }
1375  }
1376}
1377
1378/// level order, the impl is different from webpack, since we can't iterate a set and mutate it at
1379/// the same time.
1380pub fn assign_depths<'a>(
1381  assign_map: &mut IdentifierMap<usize>,
1382  modules: impl Iterator<Item = &'a ModuleIdentifier>,
1383  outgoings: &IdentifierMap<Vec<ModuleIdentifier>>,
1384) {
1385  // https://github.com/webpack/webpack/blob/1f99ad6367f2b8a6ef17cce0e058f7a67fb7db18/lib/Compilation.js#L3720
1386  let mut q = VecDeque::new();
1387  for item in modules {
1388    q.push_back((*item, 0));
1389  }
1390  while let Some((id, depth)) = q.pop_front() {
1391    match assign_map.entry(id) {
1392      hash_map::Entry::Occupied(_) => {
1393        continue;
1394      }
1395      hash_map::Entry::Vacant(vac) => {
1396        vac.insert(depth);
1397      }
1398    };
1399    for con in outgoings.get(&id).expect("should have outgoings").iter() {
1400      q.push_back((*con, depth + 1));
1401    }
1402  }
1403}
1404
1405pub fn set_depth_if_lower(
1406  module_id: ModuleIdentifier,
1407  depth: usize,
1408  assign_map: &mut IdentifierMap<usize>,
1409) -> bool {
1410  let Some(&cur_depth) = assign_map.get(&module_id) else {
1411    assign_map.insert(module_id, depth);
1412    return true;
1413  };
1414  if cur_depth > depth {
1415    assign_map.insert(module_id, depth);
1416    return true;
1417  }
1418  false
1419}
1420
1421#[derive(Debug, Clone)]
1422pub struct RenderManifestEntry {
1423  pub source: BoxSource,
1424  pub filename: String,
1425  pub has_filename: bool, /* webpack only asset has filename, js/css/wasm has filename template */
1426  pub info: AssetInfo,
1427  pub auxiliary: bool,
1428}
1429
1430#[cacheable]
1431#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
1432pub enum ManifestAssetType {
1433  #[default]
1434  Unknown,
1435  Asset,
1436  Css,
1437  JavaScript,
1438  Wasm,
1439  Custom(#[cacheable(with=AsPreset)] Ustr),
1440}
1441
1442impl fmt::Display for ManifestAssetType {
1443  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1444    match self {
1445      ManifestAssetType::Unknown => write!(f, "unknown"),
1446      ManifestAssetType::Asset => write!(f, "asset"),
1447      ManifestAssetType::Css => write!(f, "css"),
1448      ManifestAssetType::JavaScript => write!(f, "javascript"),
1449      ManifestAssetType::Wasm => write!(f, "wasm"),
1450      ManifestAssetType::Custom(custom) => write!(f, "{custom}"),
1451    }
1452  }
1453}
1454
1455impl From<String> for ManifestAssetType {
1456  fn from(value: String) -> Self {
1457    match value.as_str() {
1458      "unknown" => ManifestAssetType::Unknown,
1459      "asset" => ManifestAssetType::Asset,
1460      "css" => ManifestAssetType::Css,
1461      "javascript" => ManifestAssetType::JavaScript,
1462      "wasm" => ManifestAssetType::Wasm,
1463      _ => ManifestAssetType::Custom(value.into()),
1464    }
1465  }
1466}