Skip to main content

rspack_core/module_graph/
mod.rs

1pub mod internal;
2pub mod rollback;
3
4use std::hash::BuildHasherDefault;
5
6use internal::try_get_module_graph_module_mut_by_identifier;
7use rayon::prelude::*;
8use rspack_collections::{IdentifierHasher, IdentifierMap};
9use rspack_error::Result;
10use rspack_hash::RspackHashDigest;
11use rustc_hash::{FxHashMap as HashMap, FxHasher};
12use swc_core::ecma::atoms::Atom;
13
14use crate::{
15  AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, AsyncDependenciesBlockIdentifierMap,
16  AsyncModulesArtifact, Compilation, DependenciesBlock, Dependency, ExportInfo,
17  ImportedByDeferModulesArtifact, ModuleGraphCacheArtifact, RuntimeSpec, SideEffectsStateArtifact,
18  UsedNameItem,
19};
20mod module;
21pub use module::*;
22mod connection;
23pub use connection::*;
24
25use crate::{
26  BoxDependency, BoxModule, DependencyCondition, DependencyId, ExportsInfoArtifact,
27  ModuleIdentifier,
28};
29
30// TODO Here request can be used Atom
31pub type ImportVarMap = HashMap<(Option<ModuleIdentifier>, bool), String /* import_var */>;
32
33pub type BuildDependency = (
34  DependencyId,
35  Option<ModuleIdentifier>, /* parent module */
36);
37
38/// https://github.com/webpack/webpack/blob/ac7e531436b0d47cd88451f497cdfd0dad41535d/lib/ModuleGraph.js#L742-L748
39#[derive(Debug, Clone)]
40pub struct DependencyExtraMeta {
41  pub ids: Vec<Atom>,
42  pub explanation: Option<&'static str>,
43}
44
45#[derive(Debug, Default, Clone)]
46pub struct DependencyParents {
47  pub block: Option<AsyncDependenciesBlockIdentifier>,
48  pub module: ModuleIdentifier,
49  pub index_in_block: usize,
50}
51
52#[derive(Debug, Default)]
53pub struct IncomingConnectionsByOriginModule<'a> {
54  non_modules: Vec<&'a ModuleGraphConnection>,
55  modules: IdentifierMap<Vec<&'a ModuleGraphConnection>>,
56}
57
58impl<'a> IncomingConnectionsByOriginModule<'a> {
59  fn with_capacity(capacity: usize) -> Self {
60    Self {
61      non_modules: Vec::new(),
62      modules: IdentifierMap::with_capacity_and_hasher(capacity, Default::default()),
63    }
64  }
65
66  fn push(
67    &mut self,
68    origin_module: Option<ModuleIdentifier>,
69    connection: &'a ModuleGraphConnection,
70  ) {
71    if let Some(origin_module) = origin_module {
72      self
73        .modules
74        .entry(origin_module)
75        .or_default()
76        .push(connection);
77    } else {
78      self.non_modules.push(connection);
79    }
80  }
81
82  pub fn non_modules(&self) -> &[&'a ModuleGraphConnection] {
83    &self.non_modules
84  }
85
86  pub fn modules(&self) -> &IdentifierMap<Vec<&'a ModuleGraphConnection>> {
87    &self.modules
88  }
89
90  pub fn into_parts(
91    self,
92  ) -> (
93    Vec<&'a ModuleGraphConnection>,
94    IdentifierMap<Vec<&'a ModuleGraphConnection>>,
95  ) {
96    (self.non_modules, self.modules)
97  }
98}
99
100/// Internal data structure for ModuleGraph
101/// There're 3 kinds of data in module_graph here
102/// 1. Data only setting during Make Phase which no need for clone or overlay to recover
103/// 2. Data modified during Seal Phase which no need for clone or overlay to recover
104/// 3. Data modified both during Seal Phase and Make Phase which need for clone or overlay to recover
105///    3.1 only the contained modified in seal phase which can be reverted
106///    3.2 the item is modified which can only use overlay or clone to recover
107#[derive(Debug, Default)]
108pub(crate) struct ModuleGraphData {
109  /****** only modified during Make Phase */
110  /// Module indexed by `ModuleIdentifier`.
111  pub(crate) modules:
112    rollback::RollbackMap<ModuleIdentifier, BoxModule, BuildHasherDefault<IdentifierHasher>>,
113
114  /// Dependencies indexed by `DependencyId`.
115  dependencies: HashMap<DependencyId, BoxDependency>,
116  /// AsyncDependenciesBlocks indexed by `AsyncDependenciesBlockIdentifier`.
117  blocks: AsyncDependenciesBlockIdentifierMap<Box<AsyncDependenciesBlock>>,
118
119  /// Dependency_id to parent module identifier and parent block
120  ///
121  /// # Example
122  ///
123  /// ```ignore
124  /// let parent_module_id = parent_module.identifier();
125  /// parent_module
126  ///   .get_dependencies()
127  ///   .iter()
128  ///   .map(|dependency_id| {
129  ///     let parents_info = module_graph_partial
130  ///       .dependency_id_to_parents
131  ///       .get(dependency_id)
132  ///       .unwrap();
133  ///     assert_eq!(parents_info.module, parent_module_id);
134  ///   })
135  /// ```
136  dependency_id_to_parents: HashMap<DependencyId, DependencyParents>,
137  // TODO try move condition as connection field
138  connection_to_condition: HashMap<DependencyId, DependencyCondition>,
139
140  /************************** Modified by Seal Phase **********************/
141  /// ModuleGraphModule indexed by `ModuleIdentifier`.
142  /// modified here https://github.com/web-infra-dev/rspack/blob/9ae2f0f3be22370197cd9ed3308982f84f2bb738/crates/rspack_core/src/compilation/build_chunk_graph/code_splitter.rs#L1216
143  module_graph_modules:
144    rollback::OverlayMap<ModuleIdentifier, ModuleGraphModule, BuildHasherDefault<IdentifierHasher>>,
145
146  /// ModuleGraphConnection indexed by `DependencyId`.
147  /// modified here https://github.com/web-infra-dev/rspack/blob/9ae2f0f3be22370197cd9ed3308982f84f2bb738/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs#L820
148  connections:
149    rollback::OverlayMap<DependencyId, ModuleGraphConnection, BuildHasherDefault<FxHasher>>,
150
151  /***************** only Modified during Seal Phase ********************/
152  // setting here https://github.com/web-infra-dev/rspack/blob/9ae2f0f3be22370197cd9ed3308982f84f2bb738/crates/rspack_plugin_javascript/src/plugin/side_effects_flag_plugin.rs#L318
153  dep_meta_map: HashMap<DependencyId, DependencyExtraMeta>,
154}
155impl ModuleGraphData {
156  fn checkpoint(&mut self) {
157    self.modules.checkpoint();
158    self.module_graph_modules.checkpoint();
159    self.connections.checkpoint();
160    // dep_meta_map is not used for build_module_graph
161  }
162  // reset to checkpoint
163  fn recover(&mut self) {
164    self.modules.reset();
165    self.module_graph_modules.reset();
166    self.connections.reset();
167    // reset data to save memory
168    self.dep_meta_map.clear();
169  }
170}
171
172#[derive(Debug, Default)]
173pub struct ModuleGraph {
174  pub(super) inner: ModuleGraphData,
175}
176impl ModuleGraph {
177  // checkpoint
178  pub fn checkpoint(&mut self) {
179    self.inner.checkpoint()
180  }
181  // reset to last checkpoint
182  pub fn reset(&mut self) {
183    self.inner.recover()
184  }
185}
186
187impl ModuleGraph {
188  #[inline]
189  pub fn modules_len(&self) -> usize {
190    self.inner.modules.len()
191  }
192
193  #[inline]
194  pub fn modules(&self) -> impl Iterator<Item = (&ModuleIdentifier, &BoxModule)> {
195    self.inner.modules.iter()
196  }
197
198  #[inline]
199  pub fn modules_par(
200    &self,
201  ) -> impl rayon::prelude::ParallelIterator<Item = (&ModuleIdentifier, &BoxModule)> {
202    self.inner.modules.par_iter()
203  }
204
205  #[inline]
206  pub fn modules_keys(&self) -> impl Iterator<Item = &ModuleIdentifier> {
207    self.inner.modules.iter().map(|(k, _)| k)
208  }
209
210  #[inline]
211  pub fn module_graph_modules(
212    &self,
213  ) -> impl Iterator<Item = (&ModuleIdentifier, &ModuleGraphModule)> {
214    self.inner.module_graph_modules.iter()
215  }
216
217  // #[tracing::instrument(skip_all, fields(module = ?module_id))]
218  pub fn get_outcoming_connections_by_module(
219    &self,
220    module_id: &ModuleIdentifier,
221  ) -> IdentifierMap<Vec<&ModuleGraphConnection>> {
222    let connections = self
223      .module_graph_module_by_identifier(module_id)
224      .expect("should have mgm")
225      .outgoing_connections();
226
227    let mut map: IdentifierMap<Vec<&ModuleGraphConnection>> =
228      IdentifierMap::with_capacity_and_hasher(connections.len(), Default::default());
229    for dep_id in connections {
230      let con = self
231        .connection_by_dependency_id(dep_id)
232        .expect("should have connection");
233      map.entry(*con.module_identifier()).or_default().push(con);
234    }
235    map
236  }
237
238  pub fn get_active_outcoming_connections_by_module(
239    &self,
240    module_id: &ModuleIdentifier,
241    runtime: Option<&RuntimeSpec>,
242    module_graph: &ModuleGraph,
243    module_graph_cache: &ModuleGraphCacheArtifact,
244    side_effects_state_artifact: &SideEffectsStateArtifact,
245    exports_info_artifact: &ExportsInfoArtifact,
246  ) -> IdentifierMap<Vec<&ModuleGraphConnection>> {
247    let connections = self
248      .module_graph_module_by_identifier(module_id)
249      .expect("should have mgm")
250      .outgoing_connections();
251
252    let mut map: IdentifierMap<Vec<&ModuleGraphConnection>> =
253      IdentifierMap::with_capacity_and_hasher(connections.len(), Default::default());
254    for dep_id in connections {
255      let con = self
256        .connection_by_dependency_id(dep_id)
257        .expect("should have connection");
258      if !con.is_active(
259        module_graph,
260        runtime,
261        module_graph_cache,
262        side_effects_state_artifact,
263        exports_info_artifact,
264      ) {
265        continue;
266      }
267      map.entry(*con.module_identifier()).or_default().push(con);
268    }
269    map
270  }
271
272  // #[tracing::instrument(skip_all, fields(module = ?module_id))]
273  pub fn get_incoming_connections_by_origin_module(
274    &self,
275    module_id: &ModuleIdentifier,
276  ) -> IncomingConnectionsByOriginModule<'_> {
277    let connections = self
278      .module_graph_module_by_identifier(module_id)
279      .expect("should have mgm")
280      .incoming_connections();
281
282    let mut map = IncomingConnectionsByOriginModule::with_capacity(connections.len());
283    for dep_id in connections {
284      let con = self
285        .connection_by_dependency_id(dep_id)
286        .expect("should have connection");
287      map.push(con.original_module_identifier, con);
288    }
289    map
290  }
291
292  /// Remove dependency in mgm and target connection, return dependency_id and origin module identifier
293  ///
294  /// force will completely remove dependency, and you will not regenerate it from dependency_id
295  pub fn revoke_dependency(
296    &mut self,
297    dep_id: &DependencyId,
298    force: bool,
299  ) -> Option<BuildDependency> {
300    let original_module_identifier = self.get_parent_module(dep_id).copied();
301    let module_identifier = self.module_identifier_by_dependency_id(dep_id).copied();
302    let parent_block = self.get_parent_block(dep_id).copied();
303
304    if module_identifier.is_some() {
305      self.inner.connections.remove(dep_id);
306    }
307    if force {
308      self.inner.dependencies.remove(dep_id);
309      self.inner.dependency_id_to_parents.remove(dep_id);
310      self.inner.connection_to_condition.remove(dep_id);
311      if let Some(m_id) = original_module_identifier
312        && let Some(module) = self.inner.modules.get_mut(&m_id)
313      {
314        module.remove_dependency_id(*dep_id);
315      }
316      if let Some(b_id) = parent_block
317        && let Some(block) = self.inner.blocks.get_mut(&b_id)
318      {
319        block.remove_dependency_id(*dep_id);
320      }
321    }
322
323    // remove outgoing from original module graph module
324    if let Some(original_module_identifier) = &original_module_identifier
325      && let Some(mgm) =
326        try_get_module_graph_module_mut_by_identifier(self, original_module_identifier)
327    {
328      mgm.remove_outgoing_connection(dep_id);
329      if force {
330        mgm.all_dependencies_mut().retain(|id| id != dep_id);
331      }
332    }
333    // remove incoming from module graph module
334    if let Some(module_identifier) = &module_identifier
335      && let Some(mgm) = try_get_module_graph_module_mut_by_identifier(self, module_identifier)
336    {
337      mgm.remove_incoming_connection(dep_id);
338    }
339
340    Some((*dep_id, original_module_identifier))
341  }
342
343  pub fn revoke_module(&mut self, module_id: &ModuleIdentifier) -> Vec<BuildDependency> {
344    let blocks = self
345      .module_by_identifier(module_id)
346      .map(|m| Vec::from(m.get_blocks()))
347      .unwrap_or_default();
348
349    let (incoming_connections, all_dependencies) = self
350      .module_graph_module_by_identifier(module_id)
351      .map(|mgm| {
352        (
353          mgm.incoming_connections().clone(),
354          mgm.all_dependencies().to_vec(),
355        )
356      })
357      .unwrap_or_default();
358
359    self.inner.modules.remove(module_id);
360    self.inner.module_graph_modules.remove(module_id);
361
362    for block in blocks {
363      self.inner.blocks.remove(&block);
364    }
365
366    for dep_id in all_dependencies {
367      self.revoke_dependency(&dep_id, true);
368    }
369
370    incoming_connections
371      .iter()
372      .filter_map(|dep_id| self.revoke_dependency(dep_id, false))
373      .collect()
374  }
375
376  pub fn add_module_graph_module(&mut self, module_graph_module: ModuleGraphModule) {
377    self
378      .inner
379      .module_graph_modules
380      .insert(module_graph_module.module_identifier, module_graph_module);
381  }
382
383  /// Make sure both source and target module are exists in module graph
384  pub fn clone_module_attributes(
385    compilation: &mut Compilation,
386    source_module: &ModuleIdentifier,
387    target_module: &ModuleIdentifier,
388  ) {
389    let module_graph = compilation.get_module_graph_mut();
390    let old_mgm = module_graph
391      .module_graph_module_by_identifier(source_module)
392      .expect("should have mgm");
393
394    // Using this tuple to avoid violating rustc borrow rules
395    let assign_tuple = (
396      old_mgm.post_order_index,
397      old_mgm.pre_order_index,
398      old_mgm.depth,
399    );
400    let new_mgm = module_graph.module_graph_module_by_identifier_mut(target_module);
401    new_mgm.post_order_index = assign_tuple.0;
402    new_mgm.pre_order_index = assign_tuple.1;
403    new_mgm.depth = assign_tuple.2;
404
405    let exports_info = compilation
406      .exports_info_artifact
407      .get_exports_info(source_module);
408    compilation
409      .exports_info_artifact
410      .set_exports_info(*target_module, exports_info);
411
412    let is_async = ModuleGraph::is_async(&compilation.async_modules_artifact, source_module);
413    ModuleGraph::set_async(
414      &mut compilation.async_modules_artifact,
415      *target_module,
416      is_async,
417    );
418  }
419
420  pub fn move_module_connections(
421    &mut self,
422    old_module: &ModuleIdentifier,
423    new_module: &ModuleIdentifier,
424    filter_connection: impl Fn(&ModuleGraphConnection, &Box<dyn Dependency>) -> bool,
425  ) {
426    if old_module == new_module {
427      return;
428    }
429
430    // Outgoing connections
431    let outgoing_connections = self
432      .module_graph_module_by_identifier(old_module)
433      .expect("should have mgm")
434      .outgoing_connections()
435      .clone();
436    let mut affected_outgoing_connection = vec![];
437    for dep_id in outgoing_connections {
438      let connection = self
439        .connection_by_dependency_id(&dep_id)
440        .expect("should have connection");
441      let dependency = self.dependency_by_id(&dep_id);
442      if filter_connection(connection, dependency) {
443        let connection = self
444          .connection_by_dependency_id_mut(&dep_id)
445          .expect("should have connection");
446        connection.original_module_identifier = Some(*new_module);
447        affected_outgoing_connection.push(dep_id);
448      }
449    }
450
451    let old_mgm = self.module_graph_module_by_identifier_mut(old_module);
452    for dep_id in &affected_outgoing_connection {
453      old_mgm.remove_outgoing_connection(dep_id);
454    }
455
456    let new_mgm = self.module_graph_module_by_identifier_mut(new_module);
457    for dep_id in affected_outgoing_connection {
458      new_mgm.add_outgoing_connection(dep_id);
459    }
460
461    // Incoming connections
462    let incoming_connections = self
463      .module_graph_module_by_identifier(old_module)
464      .expect("should have mgm")
465      .incoming_connections()
466      .clone();
467    let mut affected_incoming_connection = vec![];
468    for dep_id in incoming_connections {
469      let connection = self
470        .connection_by_dependency_id(&dep_id)
471        .expect("should have connection");
472      let dependency = self.dependency_by_id(&dep_id);
473      if filter_connection(connection, dependency) {
474        let connection = self
475          .connection_by_dependency_id_mut(&dep_id)
476          .expect("should have connection");
477        connection.set_module_identifier(*new_module);
478        affected_incoming_connection.push(dep_id);
479      }
480    }
481
482    let old_mgm = self.module_graph_module_by_identifier_mut(old_module);
483    for dep_id in &affected_incoming_connection {
484      old_mgm.remove_incoming_connection(dep_id);
485    }
486
487    let new_mgm = self.module_graph_module_by_identifier_mut(new_module);
488    for dep_id in affected_incoming_connection {
489      new_mgm.add_incoming_connection(dep_id);
490    }
491  }
492
493  pub fn copy_outgoing_module_connections<F>(
494    &mut self,
495    old_module: &ModuleIdentifier,
496    new_module: &ModuleIdentifier,
497    filter_connection: F,
498  ) where
499    F: Fn(&ModuleGraphConnection, &BoxDependency) -> bool,
500  {
501    if old_module == new_module {
502      return;
503    }
504
505    let old_mgm_connections = self
506      .module_graph_module_by_identifier(old_module)
507      .expect("should have mgm")
508      .outgoing_connections()
509      .clone();
510
511    // Outgoing connections
512    let mut affected_outgoing_connections = vec![];
513    for dep_id in old_mgm_connections {
514      let connection = self
515        .connection_by_dependency_id(&dep_id)
516        .expect("should have connection");
517      let dep = self.dependency_by_id(&dep_id);
518      if filter_connection(connection, dep) {
519        let con = self
520          .connection_by_dependency_id_mut(&dep_id)
521          .expect("should have connection");
522        con.original_module_identifier = Some(*new_module);
523        affected_outgoing_connections.push(dep_id);
524      }
525    }
526
527    let new_mgm = self.module_graph_module_by_identifier_mut(new_module);
528    for dep_id in affected_outgoing_connections {
529      new_mgm.add_outgoing_connection(dep_id);
530    }
531  }
532
533  pub fn get_depth(&self, module_id: &ModuleIdentifier) -> Option<usize> {
534    self
535      .module_graph_module_by_identifier(module_id)
536      .and_then(|mgm| mgm.depth)
537  }
538
539  pub fn set_depth_if_lower(&mut self, module_id: &ModuleIdentifier, depth: usize) -> bool {
540    let mgm = self.module_graph_module_by_identifier_mut(module_id);
541    match mgm.depth {
542      Some(cur_depth) if cur_depth <= depth => false,
543      _ => {
544        mgm.depth = Some(depth);
545        true
546      }
547    }
548  }
549
550  pub fn add_module(&mut self, module: BoxModule) {
551    self.inner.modules.insert(module.identifier(), module);
552  }
553
554  pub fn add_block(&mut self, block: Box<AsyncDependenciesBlock>) {
555    self.inner.blocks.insert(block.identifier(), block);
556  }
557
558  pub fn set_parents(&mut self, dependency_id: DependencyId, parents: DependencyParents) {
559    self
560      .inner
561      .dependency_id_to_parents
562      .insert(dependency_id, parents);
563  }
564
565  pub fn get_parent_module(&self, dependency_id: &DependencyId) -> Option<&ModuleIdentifier> {
566    self
567      .inner
568      .dependency_id_to_parents
569      .get(dependency_id)
570      .map(|p| &p.module)
571  }
572
573  pub fn get_parent_block(
574    &self,
575    dependency_id: &DependencyId,
576  ) -> Option<&AsyncDependenciesBlockIdentifier> {
577    self
578      .inner
579      .dependency_id_to_parents
580      .get(dependency_id)
581      .and_then(|p| p.block.as_ref())
582  }
583
584  pub fn get_parent_block_index(&self, dependency_id: &DependencyId) -> Option<usize> {
585    self
586      .inner
587      .dependency_id_to_parents
588      .get(dependency_id)
589      .map(|p| p.index_in_block)
590  }
591
592  pub fn block_by_id(
593    &self,
594    block_id: &AsyncDependenciesBlockIdentifier,
595  ) -> Option<&AsyncDependenciesBlock> {
596    self.inner.blocks.get(block_id).map(AsRef::as_ref)
597  }
598
599  pub fn block_by_id_expect(
600    &self,
601    block_id: &AsyncDependenciesBlockIdentifier,
602  ) -> &AsyncDependenciesBlock {
603    self
604      .inner
605      .blocks
606      .get(block_id)
607      .expect("should insert block before get it")
608  }
609
610  pub fn blocks(&self) -> &AsyncDependenciesBlockIdentifierMap<Box<AsyncDependenciesBlock>> {
611    &self.inner.blocks
612  }
613
614  pub fn dependencies(&self) -> impl Iterator<Item = (&DependencyId, &BoxDependency)> {
615    self.inner.dependencies.iter()
616  }
617
618  pub fn add_dependency(&mut self, dependency: BoxDependency) {
619    self.inner.dependencies.insert(*dependency.id(), dependency);
620  }
621
622  /// Get a dependency by ID, panicking if not found.
623  ///
624  /// **PREFERRED METHOD**: Use this for ALL internal Rust code including:
625  /// - Core compilation logic
626  /// - All plugins (`rspack_plugin_*`)
627  /// - Stats generation, code generation, runtime templates
628  /// - Chunk graph building, export analysis
629  /// - Module graph building operations
630  /// - Any internal operations where dependencies should exist
631  ///
632  /// Dependencies should always be accessible in internal operations, so this
633  /// method enforces that invariant with a clear panic message if violated.
634  ///
635  /// **Only the binding layer (`rspack_binding_api`) should use `internal::try_dependency_by_id()`**
636  /// for graceful handling of missing dependencies in external APIs.
637  pub fn dependency_by_id(&self, dependency_id: &DependencyId) -> &BoxDependency {
638    self
639      .inner
640      .dependencies
641      .get(dependency_id)
642      .unwrap_or_else(|| panic!("Dependency with ID {dependency_id:?} not found"))
643  }
644
645  /// Get a mutable dependency by ID, panicking if not found.
646  ///
647  /// **PREFERRED METHOD**: Use this for ALL internal Rust code when you need to
648  /// modify dependencies. Dependencies should always be accessible in internal
649  /// operations, so this method enforces that invariant with a clear panic message.
650  pub fn dependency_by_id_mut(&mut self, dependency_id: &DependencyId) -> &mut BoxDependency {
651    self
652      .inner
653      .dependencies
654      .get_mut(dependency_id)
655      .unwrap_or_else(|| panic!("Dependency with ID {dependency_id:?} not found"))
656  }
657
658  /// Uniquely identify a module by its dependency
659  pub fn module_graph_module_by_dependency_id(
660    &self,
661    id: &DependencyId,
662  ) -> Option<&ModuleGraphModule> {
663    self
664      .module_identifier_by_dependency_id(id)
665      .and_then(|module_identifier| self.module_graph_module_by_identifier(module_identifier))
666  }
667
668  pub fn module_identifier_by_dependency_id(
669    &self,
670    dep_id: &DependencyId,
671  ) -> Option<&ModuleIdentifier> {
672    self
673      .inner
674      .connections
675      .get(dep_id)
676      .map(|con| con.module_identifier())
677  }
678
679  pub fn get_module_by_dependency_id(&self, dep_id: &DependencyId) -> Option<&BoxModule> {
680    self
681      .module_identifier_by_dependency_id(dep_id)
682      .and_then(|module_id| self.inner.modules.get(module_id))
683  }
684
685  fn add_connection(
686    &mut self,
687    connection: ModuleGraphConnection,
688    condition: Option<DependencyCondition>,
689  ) {
690    if self
691      .connection_by_dependency_id(&connection.dependency_id)
692      .is_some()
693    {
694      return;
695    }
696
697    if let Some(condition) = condition {
698      self
699        .inner
700        .connection_to_condition
701        .insert(connection.dependency_id, condition);
702    }
703
704    let module_id = *connection.module_identifier();
705    let origin_module_id = connection.original_module_identifier;
706    let dependency_id = connection.dependency_id;
707
708    // add to connections list
709    self
710      .inner
711      .connections
712      .insert(connection.dependency_id, connection);
713
714    // set to module incoming connection
715    {
716      let mgm = self.module_graph_module_by_identifier_mut(&module_id);
717
718      mgm.add_incoming_connection(dependency_id);
719    }
720
721    // set to origin module outgoing connection
722    if let Some(identifier) = origin_module_id {
723      let original_mgm = self.module_graph_module_by_identifier_mut(&identifier);
724      original_mgm.add_outgoing_connection(dependency_id);
725    };
726  }
727
728  /// Add a connection between two module graph modules, if a connection exists, then it will be reused.
729  pub fn set_resolved_module(
730    &mut self,
731    original_module_identifier: Option<ModuleIdentifier>,
732    dependency_id: DependencyId,
733    module_identifier: ModuleIdentifier,
734  ) -> Result<()> {
735    let dependency = self.dependency_by_id(&dependency_id);
736    let is_module_dependency =
737      dependency.as_module_dependency().is_some() || dependency.as_context_dependency().is_some();
738    let condition = dependency
739      .as_module_dependency()
740      .and_then(|dep| dep.get_condition());
741    if !is_module_dependency {
742      return Ok(());
743    }
744
745    let conditional = condition.is_some();
746    let new_connection = ModuleGraphConnection::new(
747      dependency_id,
748      original_module_identifier,
749      module_identifier,
750      conditional,
751    );
752    self.add_connection(new_connection, condition);
753
754    Ok(())
755  }
756
757  /// Uniquely identify a module by its identifier and return the aliased reference
758  pub fn module_by_identifier(&self, identifier: &ModuleIdentifier) -> Option<&BoxModule> {
759    self.inner.modules.get(identifier)
760  }
761
762  pub fn module_by_identifier_mut(
763    &mut self,
764    identifier: &ModuleIdentifier,
765  ) -> Option<&mut BoxModule> {
766    self.inner.modules.get_mut(identifier)
767  }
768
769  /// Uniquely identify a module graph module by its module's identifier and return the aliased reference
770  pub fn module_graph_module_by_identifier(
771    &self,
772    identifier: &ModuleIdentifier,
773  ) -> Option<&ModuleGraphModule> {
774    self.inner.module_graph_modules.get(identifier)
775  }
776
777  /// Get a mutable module graph module by identifier, panicking if not found.
778  ///
779  /// **PREFERRED METHOD**: Use this for all internal code where the module graph module
780  /// should exist. This enforces the invariant with a clear panic message if violated.
781  ///
782  /// Only use `try_module_graph_module_by_identifier_mut()` when you need to handle missing modules.
783  pub fn module_graph_module_by_identifier_mut(
784    &mut self,
785    identifier: &ModuleIdentifier,
786  ) -> &mut ModuleGraphModule {
787    self
788      .inner
789      .module_graph_modules
790      .get_mut(identifier)
791      .unwrap_or_else(|| panic!("ModuleGraphModule with identifier {identifier:?} not found"))
792  }
793
794  pub fn get_ordered_outgoing_connections(
795    &self,
796    module_identifier: &ModuleIdentifier,
797  ) -> impl Iterator<Item = &ModuleGraphConnection> {
798    self
799      .module_graph_module_by_identifier(module_identifier)
800      .map(|m| {
801        m.all_dependencies()
802          .iter()
803          .filter_map(|dep_id| self.connection_by_dependency_id(dep_id))
804      })
805      .into_iter()
806      .flatten()
807  }
808
809  pub fn get_outgoing_deps_in_order(
810    &self,
811    module_identifier: &ModuleIdentifier,
812  ) -> impl Iterator<Item = &DependencyId> {
813    self
814      .module_graph_module_by_identifier(module_identifier)
815      .map(|m| {
816        m.all_dependencies()
817          .iter()
818          .filter(|dep_id| self.connection_by_dependency_id(dep_id).is_some())
819      })
820      .into_iter()
821      .flatten()
822  }
823
824  pub fn connection_by_dependency_id(
825    &self,
826    dependency_id: &DependencyId,
827  ) -> Option<&ModuleGraphConnection> {
828    self.inner.connections.get(dependency_id)
829  }
830
831  pub fn get_resolved_module(&self, dependency_id: &DependencyId) -> Option<&ModuleIdentifier> {
832    match self.connection_by_dependency_id(dependency_id) {
833      Some(connection) => Some(&connection.resolved_module),
834      None => None,
835    }
836  }
837
838  pub fn connection_by_dependency_id_mut(
839    &mut self,
840    dependency_id: &DependencyId,
841  ) -> Option<&mut ModuleGraphConnection> {
842    self.inner.connections.get_mut(dependency_id)
843  }
844
845  pub fn get_pre_order_index(&self, module_id: &ModuleIdentifier) -> Option<u32> {
846    self
847      .module_graph_module_by_identifier(module_id)
848      .and_then(|mgm| mgm.pre_order_index)
849  }
850
851  pub fn get_post_order_index(&self, module_id: &ModuleIdentifier) -> Option<u32> {
852    self
853      .module_graph_module_by_identifier(module_id)
854      .and_then(|mgm| mgm.post_order_index)
855  }
856
857  pub fn get_issuer(&self, module_id: &ModuleIdentifier) -> Option<&BoxModule> {
858    self
859      .module_graph_module_by_identifier(module_id)
860      .and_then(|mgm| mgm.issuer().get_module(self))
861  }
862
863  pub fn is_optional(
864    &self,
865    module_id: &ModuleIdentifier,
866    module_graph_cache: &ModuleGraphCacheArtifact,
867    side_effects_state_artifact: &SideEffectsStateArtifact,
868    exports_info_artifact: &ExportsInfoArtifact,
869  ) -> bool {
870    let mut has_connections = false;
871    for connection in self.get_incoming_connections(module_id) {
872      let dependency = self.dependency_by_id(&connection.dependency_id);
873      let Some(module_dependency) = dependency.as_module_dependency() else {
874        return false;
875      };
876      if !module_dependency.get_optional()
877        || !connection.is_target_active(
878          self,
879          None,
880          module_graph_cache,
881          side_effects_state_artifact,
882          exports_info_artifact,
883        )
884      {
885        return false;
886      }
887      has_connections = true;
888    }
889    has_connections
890  }
891
892  pub fn is_async(
893    async_modules_artifact: &AsyncModulesArtifact,
894    module_id: &ModuleIdentifier,
895  ) -> bool {
896    async_modules_artifact.contains(module_id)
897  }
898
899  pub fn is_deferred(
900    &self,
901    imported_by_defer_modules_artifact: &ImportedByDeferModulesArtifact,
902    module_id: &ModuleIdentifier,
903  ) -> bool {
904    let imported_by_defer = imported_by_defer_modules_artifact.contains(module_id);
905    if !imported_by_defer {
906      return false;
907    }
908    let module = self
909      .module_by_identifier(module_id)
910      .expect("should have module");
911    !module.build_meta().has_top_level_await
912  }
913
914  pub fn set_async(
915    async_modules_artifact: &mut AsyncModulesArtifact,
916    module_id: ModuleIdentifier,
917    is_async: bool,
918  ) -> bool {
919    let original = Self::is_async(async_modules_artifact, &module_id);
920    if original == is_async {
921      return false;
922    }
923    if original {
924      async_modules_artifact.remove(&module_id)
925    } else {
926      async_modules_artifact.insert(module_id)
927    }
928  }
929
930  pub fn get_outgoing_connections(
931    &self,
932    module_id: &ModuleIdentifier,
933  ) -> impl Iterator<Item = &ModuleGraphConnection> + Clone {
934    self
935      .module_graph_module_by_identifier(module_id)
936      .map(|mgm| {
937        mgm
938          .outgoing_connections()
939          .iter()
940          .filter_map(|id| self.connection_by_dependency_id(id))
941      })
942      .into_iter()
943      .flatten()
944  }
945
946  pub fn get_incoming_connections(
947    &self,
948    module_id: &ModuleIdentifier,
949  ) -> impl Iterator<Item = &ModuleGraphConnection> + Clone {
950    self
951      .module_graph_module_by_identifier(module_id)
952      .map(|mgm| {
953        mgm
954          .incoming_connections()
955          .iter()
956          .filter_map(|id| self.connection_by_dependency_id(id))
957      })
958      .into_iter()
959      .flatten()
960  }
961
962  pub fn get_module_hash(&self, module_id: &ModuleIdentifier) -> Option<&RspackHashDigest> {
963    self
964      .module_by_identifier(module_id)
965      .and_then(|m| m.build_info().hash.as_ref())
966  }
967
968  /// We can't insert all sort of things into one hashmap like javascript, so we create different
969  /// hashmap to store different kinds of meta.
970  pub fn get_dep_meta_if_existing(&self, id: &DependencyId) -> Option<&DependencyExtraMeta> {
971    self.inner.dep_meta_map.get(id)
972  }
973
974  pub fn set_dependency_extra_meta(&mut self, dep_id: DependencyId, extra: DependencyExtraMeta) {
975    self.inner.dep_meta_map.insert(dep_id, extra);
976  }
977
978  pub fn can_update_module(&self, dep_id: &DependencyId, module_id: &ModuleIdentifier) -> bool {
979    let connection = self
980      .connection_by_dependency_id(dep_id)
981      .expect("should have connection");
982    connection.module_identifier() != module_id
983  }
984
985  pub fn do_update_module(&mut self, dep_id: &DependencyId, module_id: &ModuleIdentifier) {
986    let connection = self
987      .connection_by_dependency_id_mut(dep_id)
988      .unwrap_or_else(|| panic!("{dep_id:?}"));
989    let old_module_identifier = *connection.module_identifier();
990    connection.set_module_identifier(*module_id);
991
992    // remove dep_id from old module mgm incoming connection
993    let old_mgm = self.module_graph_module_by_identifier_mut(&old_module_identifier);
994    old_mgm.remove_incoming_connection(dep_id);
995
996    // add dep_id to updated module mgm incoming connection
997    let new_mgm = self.module_graph_module_by_identifier_mut(module_id);
998    new_mgm.add_incoming_connection(*dep_id);
999  }
1000
1001  pub fn get_optimization_bailout_mut(
1002    &mut self,
1003    id: &ModuleIdentifier,
1004  ) -> &mut Vec<OptimizationBailoutItem> {
1005    let mgm = self.module_graph_module_by_identifier_mut(id);
1006    mgm.optimization_bailout_mut()
1007  }
1008
1009  pub fn get_optimization_bailout(&self, id: &ModuleIdentifier) -> &Vec<OptimizationBailoutItem> {
1010    let mgm = self
1011      .module_graph_module_by_identifier(id)
1012      .expect("should have module graph module");
1013    &mgm.optimization_bailout
1014  }
1015
1016  pub fn get_condition_state(
1017    &self,
1018    connection: &ModuleGraphConnection,
1019    runtime: Option<&RuntimeSpec>,
1020    module_graph_cache: &ModuleGraphCacheArtifact,
1021    side_effects_state_artifact: &SideEffectsStateArtifact,
1022    exports_info_artifact: &ExportsInfoArtifact,
1023  ) -> ConnectionState {
1024    let condition = self
1025      .inner
1026      .connection_to_condition
1027      .get(&connection.dependency_id)
1028      .expect("should have condition");
1029    condition.get_connection_state(
1030      connection,
1031      runtime,
1032      self,
1033      module_graph_cache,
1034      side_effects_state_artifact,
1035      exports_info_artifact,
1036    )
1037  }
1038
1039  pub fn is_connection_active(
1040    &self,
1041    connection: &ModuleGraphConnection,
1042    runtime: Option<&RuntimeSpec>,
1043    module_graph_cache: &ModuleGraphCacheArtifact,
1044    side_effects_state_artifact: &SideEffectsStateArtifact,
1045    exports_info_artifact: &ExportsInfoArtifact,
1046  ) -> bool {
1047    let condition = self
1048      .inner
1049      .connection_to_condition
1050      .get(&connection.dependency_id)
1051      .expect("should have condition");
1052    condition.is_connection_active(
1053      connection,
1054      runtime,
1055      self,
1056      module_graph_cache,
1057      side_effects_state_artifact,
1058      exports_info_artifact,
1059    )
1060  }
1061
1062  // todo remove it after module_graph_partial remove all of dependency_id_to_*
1063  pub fn cache_recovery_connection(&mut self, connection: ModuleGraphConnection) {
1064    let condition = self
1065      .dependency_by_id(&connection.dependency_id)
1066      .as_module_dependency()
1067      .and_then(|dep| dep.get_condition());
1068
1069    // recovery condition
1070    if let Some(condition) = condition {
1071      self
1072        .inner
1073        .connection_to_condition
1074        .insert(connection.dependency_id, condition);
1075    }
1076
1077    self
1078      .inner
1079      .connections
1080      .insert(connection.dependency_id, connection);
1081  }
1082
1083  pub fn batch_set_connections_original_module(
1084    &mut self,
1085    tasks: Vec<(DependencyId, ModuleIdentifier)>,
1086  ) {
1087    let changed = tasks
1088      .into_par_iter()
1089      .map(|(dep_id, original_module_identifier)| {
1090        let mut con = self
1091          .connection_by_dependency_id(&dep_id)
1092          .expect("should have connection")
1093          .clone();
1094        con.original_module_identifier = Some(original_module_identifier);
1095        (dep_id, con)
1096      })
1097      .collect::<Vec<_>>();
1098
1099    for (dep_id, con) in changed {
1100      self.inner.connections.insert(dep_id, con);
1101    }
1102  }
1103
1104  pub fn batch_set_connections_module(&mut self, tasks: Vec<(DependencyId, ModuleIdentifier)>) {
1105    let changed = tasks
1106      .into_par_iter()
1107      .map(|(dep_id, module_identifier)| {
1108        let mut con = self
1109          .connection_by_dependency_id(&dep_id)
1110          .expect("should have connection")
1111          .clone();
1112        con.set_module_identifier(module_identifier);
1113        (dep_id, con)
1114      })
1115      .collect::<Vec<_>>();
1116
1117    for (dep_id, con) in changed {
1118      self.inner.connections.insert(dep_id, con);
1119    }
1120  }
1121
1122  pub fn batch_add_connections(
1123    &mut self,
1124    tasks: Vec<(ModuleIdentifier, Vec<DependencyId>, Vec<DependencyId>)>,
1125  ) {
1126    let changed = tasks
1127      .into_par_iter()
1128      .map(|(mid, outgoings, incomings)| {
1129        let mut mgm = self
1130          .module_graph_module_by_identifier(&mid)
1131          .expect("should have mgm")
1132          .clone();
1133        for outgoing in outgoings {
1134          mgm.add_outgoing_connection(outgoing);
1135        }
1136        for incoming in incomings {
1137          mgm.add_incoming_connection(incoming);
1138        }
1139        (mid, mgm)
1140      })
1141      .collect::<Vec<_>>();
1142
1143    for (mid, mgm) in changed {
1144      self.inner.module_graph_modules.insert(mid, mgm);
1145    }
1146  }
1147
1148  pub fn batch_remove_connections(
1149    &mut self,
1150    tasks: Vec<(ModuleIdentifier, Vec<DependencyId>, Vec<DependencyId>)>,
1151  ) {
1152    let changed = tasks
1153      .into_par_iter()
1154      .map(|(mid, outgoings, incomings)| {
1155        let mut mgm = self
1156          .module_graph_module_by_identifier(&mid)
1157          .expect("should have mgm")
1158          .clone();
1159        for outgoing in outgoings.iter() {
1160          mgm.remove_outgoing_connection(outgoing);
1161        }
1162        for incoming in incomings.iter() {
1163          mgm.remove_incoming_connection(incoming);
1164        }
1165        (mid, mgm)
1166      })
1167      .collect::<Vec<_>>();
1168
1169    for (mid, mgm) in changed {
1170      self.inner.module_graph_modules.insert(mid, mgm);
1171    }
1172  }
1173
1174  pub fn batch_set_export_info_used_name(
1175    &mut self,
1176    exports_info_artifact: &mut ExportsInfoArtifact,
1177    tasks: Vec<(ExportInfo, UsedNameItem)>,
1178  ) {
1179    for (export_info, used_name) in tasks {
1180      export_info
1181        .as_data_mut(exports_info_artifact)
1182        .set_used_name(used_name);
1183    }
1184  }
1185}