Skip to main content

rspack_core/
context_module.rs

1use std::{borrow::Cow, fmt::Write, hash::Hash, sync::Arc};
2
3use concat_string::concat_string;
4use cow_utils::CowUtils;
5use derive_more::Debug;
6use futures::future::BoxFuture;
7use indoc::formatdoc;
8use itertools::Itertools;
9use rspack_cacheable::{
10  cacheable, cacheable_dyn,
11  with::{AsCacheable, AsOption, AsPreset, AsVec, Unsupported},
12};
13use rspack_collections::{Identifiable, Identifier};
14use rspack_error::{Result, impl_empty_diagnosable_trait};
15use rspack_hash::{RspackHashDigest, RspackHasher};
16use rspack_macros::impl_source_map_config;
17use rspack_paths::{InternedPathSet, Utf8PathBuf};
18use rspack_regex::RspackRegex;
19use rspack_sources::{BoxSource, OriginalSource, RawStringSource, SourceExt};
20use rspack_util::{
21  fx_hash::FxIndexMap,
22  identifier::make_paths_relative,
23  itoa, json_stringify, json_stringify_pretty,
24  source_map::{ModuleSourceMapConfig, SourceMapKind},
25};
26use rustc_hash::FxHashMap as HashMap;
27
28use crate::{
29  AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, BoxDependency, BoxModule, BuildContext,
30  BuildInfo, BuildMeta, BuildMetaDefaultObject, BuildMetaExportsType, BuildResult, ChunkGraph,
31  ChunkGroupOptions, CodeGenerationResultBuilder, Compilation, Context, ContextElementDependency,
32  DependenciesBlock, DependencyCategory, DependencyId, DependencyLocation, DynamicImportMode,
33  ExportsType, FactoryMeta, FakeNamespaceObjectMode, GroupOptions, ImportAttributes, ImportPhase,
34  LibIdentOptions, Module, ModuleArgument, ModuleCodeGenerationContext, ModuleCodeTemplate,
35  ModuleGraph, ModuleId, ModuleIdsArtifact, ModuleLayer, ModuleType, RealDependencyLocation,
36  ReferencedSpecifier, Resolve, RuntimeGlobals, RuntimeGlobalsRenderMode, RuntimeSpec, SourceType,
37  contextify, get_exports_type_with_strict, get_outgoing_async_modules, impl_module_meta_info,
38  module_update_hash, property_access, to_path,
39};
40
41static CHUNK_NAME_INDEX_PLACEHOLDER: &str = "[index]";
42static CHUNK_NAME_REQUEST_PLACEHOLDER: &str = "[request]";
43
44#[cacheable]
45#[derive(Debug, PartialEq, Eq, Clone, Hash)]
46pub enum ContextMode {
47  Sync,
48  Eager,
49  Weak,
50  AsyncWeak,
51  Lazy,
52  LazyOnce,
53}
54
55impl ContextMode {
56  pub fn as_str(&self) -> &str {
57    match self {
58      ContextMode::Sync => "sync",
59      ContextMode::Eager => "eager",
60      ContextMode::Weak => "weak",
61      ContextMode::Lazy => "lazy",
62      ContextMode::LazyOnce => "lazy-once",
63      ContextMode::AsyncWeak => "async-weak",
64    }
65  }
66}
67
68impl From<&str> for ContextMode {
69  fn from(value: &str) -> Self {
70    match try_convert_str_to_context_mode(value) {
71      Some(m) => m,
72      // TODO should give warning
73      _ => panic!("unknown context mode"),
74    }
75  }
76}
77
78impl From<DynamicImportMode> for ContextMode {
79  fn from(value: DynamicImportMode) -> Self {
80    match value {
81      DynamicImportMode::Lazy => Self::Lazy,
82      DynamicImportMode::Weak => Self::AsyncWeak,
83      DynamicImportMode::Eager => Self::Eager,
84      DynamicImportMode::LazyOnce => Self::LazyOnce,
85    }
86  }
87}
88
89pub fn try_convert_str_to_context_mode(s: &str) -> Option<ContextMode> {
90  match s {
91    "sync" => Some(ContextMode::Sync),
92    "eager" => Some(ContextMode::Eager),
93    "weak" => Some(ContextMode::Weak),
94    "lazy" => Some(ContextMode::Lazy),
95    "lazy-once" => Some(ContextMode::LazyOnce),
96    "async-weak" => Some(ContextMode::AsyncWeak),
97    // TODO should give warning
98    _ => None,
99  }
100}
101
102#[cacheable]
103#[derive(Debug, Clone, PartialEq, Eq, Hash)]
104pub enum ContextNameSpaceObject {
105  Bool(bool),
106  Strict,
107  Unset,
108}
109
110impl ContextNameSpaceObject {
111  pub fn is_false(&self) -> bool {
112    matches!(self, ContextNameSpaceObject::Unset)
113      || matches!(self, ContextNameSpaceObject::Bool(v) if !v)
114  }
115}
116
117#[cacheable]
118#[derive(Debug, Clone, Copy, Hash, PartialEq, PartialOrd, Ord, Eq)]
119pub enum ContextTypePrefix {
120  Import,
121  Normal,
122}
123
124#[cacheable]
125#[derive(Debug, Clone)]
126pub enum ContextModulePattern {
127  None,
128  RegExp(RspackRegex),
129  Glob(Vec<String>),
130}
131
132impl ContextModulePattern {
133  pub fn reg_exp(&self) -> Option<&RspackRegex> {
134    match self {
135      Self::RegExp(reg_exp) => Some(reg_exp),
136      Self::None | Self::Glob(_) => None,
137    }
138  }
139
140  pub fn glob_patterns(&self) -> Option<&[String]> {
141    match self {
142      Self::None | Self::RegExp(_) => None,
143      Self::Glob(glob_patterns) => Some(glob_patterns),
144    }
145  }
146
147  pub fn is_empty(&self) -> bool {
148    matches!(self, Self::None)
149  }
150
151  pub fn glob_patterns_to_string(glob_patterns: &[String]) -> String {
152    glob_patterns.iter().join(", ")
153  }
154
155  pub fn to_pretty_string(&self, source: bool) -> Option<String> {
156    match self {
157      Self::None => None,
158      Self::RegExp(reg_exp) => Some(reg_exp.to_pretty_string(source)),
159      Self::Glob(glob_patterns) => Some(Self::glob_patterns_to_string(glob_patterns)),
160    }
161  }
162}
163
164impl From<Option<RspackRegex>> for ContextModulePattern {
165  fn from(reg_exp: Option<RspackRegex>) -> Self {
166    match reg_exp {
167      Some(reg_exp) => Self::RegExp(reg_exp),
168      None => Self::None,
169    }
170  }
171}
172
173#[cacheable]
174#[derive(Debug, Clone)]
175pub struct ContextOptions {
176  pub mode: ContextMode,
177  pub recursive: bool,
178  pub pattern: ContextModulePattern,
179  pub include: Option<RspackRegex>,
180  pub exclude: Option<RspackRegex>,
181  pub category: DependencyCategory,
182  /// The context request passed to the resolver.
183  pub request: String,
184  /// The base directory used to resolve relative context requests. For glob patterns, this is the
185  /// parser-derived request context (the importer directory or the explicit `base`) and remains
186  /// the matching coordinate even when context module factory hooks relocate the scan request.
187  pub context: String,
188  /// The compiler `options.context`. Root-relative glob patterns are resolved from this directory.
189  /// This value is stable for the lifetime of a compilation and must not be changed by context
190  /// module factory hooks.
191  pub compiler_context: Context,
192  pub namespace_object: ContextNameSpaceObject,
193  pub group_options: Option<GroupOptions>,
194  pub replaces: Vec<(String, u32, u32)>,
195  pub start: u32,
196  pub end: u32,
197  #[cacheable(with=AsOption<AsVec<AsCacheable>>)]
198  pub referenced_specifiers: Option<Vec<ReferencedSpecifier>>,
199  pub glob_import: Option<String>,
200  pub glob_exhaustive: bool,
201  pub glob_case_sensitive: bool,
202  pub attributes: Option<ImportAttributes>,
203  pub phase: Option<ImportPhase>,
204}
205
206impl Default for ContextOptions {
207  fn default() -> Self {
208    Self {
209      mode: ContextMode::Sync,
210      recursive: false,
211      pattern: ContextModulePattern::None,
212      include: None,
213      exclude: None,
214      category: DependencyCategory::Unknown,
215      request: String::new(),
216      context: String::new(),
217      compiler_context: Context::default(),
218      namespace_object: ContextNameSpaceObject::Unset,
219      group_options: None,
220      replaces: Vec::new(),
221      start: 0,
222      end: 0,
223      referenced_specifiers: None,
224      glob_import: None,
225      glob_exhaustive: false,
226      glob_case_sensitive: true,
227      attributes: None,
228      phase: None,
229    }
230  }
231}
232
233/// Returns the compiler-context-relative form of a context when it needs to be part of an
234/// identifier. The compiler context itself is implicit, so encoding it as `./.` would only change
235/// existing identifiers without distinguishing different resolution behavior.
236pub fn context_identifier(compiler_context: &str, context: &str) -> Option<String> {
237  if context.is_empty() {
238    return None;
239  }
240
241  let context = contextify(compiler_context, context);
242  (context != "./.").then_some(context)
243}
244
245#[cacheable]
246#[derive(Debug, Clone)]
247pub struct ContextModuleOptions {
248  pub addon: String,
249  #[cacheable(with=AsPreset)]
250  pub resource: Utf8PathBuf,
251  pub resource_query: String,
252  pub resource_fragment: String,
253  pub context_options: ContextOptions,
254  pub layer: Option<ModuleLayer>,
255  pub resolve_options: Option<Arc<Resolve>>,
256  pub type_prefix: ContextTypePrefix,
257}
258
259#[derive(Debug)]
260pub enum FakeMapValue {
261  Bit(FakeNamespaceObjectMode),
262  Map(HashMap<String, FakeNamespaceObjectMode>),
263}
264
265struct ContextBlockInfo {
266  user_request: String,
267  dep_id: DependencyId,
268  block_id: AsyncDependenciesBlockIdentifier,
269}
270
271pub type ResolveContextModuleDependencies = Arc<
272  dyn Fn(ContextModuleOptions) -> BoxFuture<'static, Result<Vec<ContextElementDependency>>>
273    + Send
274    + Sync,
275>;
276
277#[impl_source_map_config]
278#[cacheable]
279#[derive(Debug)]
280pub struct ContextModule {
281  dependencies: Vec<DependencyId>,
282  blocks: Vec<AsyncDependenciesBlockIdentifier>,
283  identifier: Identifier,
284  options: ContextModuleOptions,
285  factory_meta: Option<FactoryMeta>,
286  build_info: BuildInfo,
287  build_meta: BuildMeta,
288  #[debug(skip)]
289  #[cacheable(with=Unsupported)]
290  resolve_dependencies: ResolveContextModuleDependencies,
291}
292
293impl ContextModule {
294  pub(crate) fn new_with_strict(
295    resolve_dependencies: ResolveContextModuleDependencies,
296    options: ContextModuleOptions,
297    strict: Option<bool>,
298  ) -> Self {
299    let mut build_info = BuildInfo::default();
300    if let Some(strict) = strict {
301      build_info.strict = strict;
302    }
303
304    Self {
305      dependencies: Vec::new(),
306      blocks: Vec::new(),
307      identifier: create_identifier(&options, None),
308      options,
309      factory_meta: None,
310      build_info,
311      build_meta: BuildMeta::default()
312        .with_exports_type(BuildMetaExportsType::Default)
313        .with_default_object(BuildMetaDefaultObject::RedirectWarn),
314      source_map_kind: SourceMapKind::empty(),
315      resolve_dependencies,
316    }
317  }
318
319  fn get_module_id<'a>(&self, module_ids: &'a ModuleIdsArtifact) -> &'a ModuleId {
320    ChunkGraph::get_module_id(module_ids, self.identifier).expect("module id not found")
321  }
322
323  pub fn get_context_options(&self) -> &ContextOptions {
324    &self.options.context_options
325  }
326
327  fn get_fake_map<'a>(
328    &self,
329    dependencies: impl IntoIterator<Item = &'a DependencyId>,
330    compilation: &Compilation,
331  ) -> FakeMapValue {
332    let dependencies = dependencies.into_iter();
333    if self.options.context_options.namespace_object.is_false() {
334      return FakeMapValue::Bit(FakeNamespaceObjectMode::NAMESPACE);
335    }
336    let mut has_type = 0;
337    let mut fake_map = HashMap::default();
338    let module_graph = compilation.get_module_graph();
339    let sorted_modules = dependencies
340      .filter_map(|dep_id| {
341        module_graph
342          .module_identifier_by_dependency_id(dep_id)
343          .map(|m| (m, dep_id))
344      })
345      .filter_map(|(m, dep)| {
346        ChunkGraph::get_module_id(&compilation.module_ids_artifact, *m)
347          .map(|id| (id.to_string(), dep))
348      })
349      .sorted_unstable_by_key(|(module_id, _)| module_id.clone());
350    for (module_id, dep) in sorted_modules {
351      let exports_type = get_exports_type_with_strict(
352        compilation.get_module_graph(),
353        &compilation.module_graph_cache_artifact,
354        &compilation.exports_info_artifact,
355        dep,
356        matches!(
357          self.options.context_options.namespace_object,
358          ContextNameSpaceObject::Strict
359        ),
360      );
361      match exports_type {
362        ExportsType::Namespace => {
363          fake_map.insert(module_id, FakeNamespaceObjectMode::NAMESPACE);
364          has_type |= 1;
365        }
366        ExportsType::Dynamic => {
367          fake_map.insert(module_id, FakeNamespaceObjectMode::DYNAMIC);
368          has_type |= 2;
369        }
370        ExportsType::DefaultOnly => {
371          fake_map.insert(module_id, FakeNamespaceObjectMode::MODULE_ID);
372          has_type |= 4;
373        }
374        ExportsType::DefaultWithNamed => {
375          fake_map.insert(module_id, FakeNamespaceObjectMode::DEFAULT_WITH_NAMED);
376          has_type |= 8;
377        }
378      }
379    }
380
381    match has_type {
382      0 | 1 => FakeMapValue::Bit(FakeNamespaceObjectMode::NAMESPACE),
383      2 => FakeMapValue::Bit(FakeNamespaceObjectMode::DYNAMIC),
384      4 => FakeMapValue::Bit(FakeNamespaceObjectMode::MODULE_ID),
385      8 => FakeMapValue::Bit(FakeNamespaceObjectMode::DEFAULT_WITH_NAMED),
386      _ => FakeMapValue::Map(fake_map),
387    }
388  }
389
390  fn get_fake_map_init_statement(&self, fake_map: &FakeMapValue) -> String {
391    match fake_map {
392      FakeMapValue::Bit(_) => String::new(),
393      FakeMapValue::Map(map) => format!("var fakeMap = {}", json_stringify_pretty(map)),
394    }
395  }
396
397  fn get_module_deferred_async_deps_map<'a>(
398    &self,
399    dependencies: impl IntoIterator<Item = &'a DependencyId>,
400    compilation: &Compilation,
401  ) -> HashMap<String, Vec<ModuleId>> {
402    let module_graph = compilation.get_module_graph();
403    let mut map: HashMap<String, Vec<ModuleId>> = HashMap::default();
404    for dep_id in dependencies {
405      if let Some(module) = module_graph.get_module_by_dependency_id(dep_id)
406        && !module.build_meta().has_top_level_await()
407      {
408        let id = ChunkGraph::get_module_id(&compilation.module_ids_artifact, module.identifier());
409        if let Some(id) = id {
410          let async_deps = get_outgoing_async_modules(compilation, module.as_ref());
411          map.insert(id.to_string(), async_deps.into_iter().collect());
412        }
413      }
414    }
415    map
416  }
417
418  fn get_module_deferred_async_deps_map_init_statement(
419    &self,
420    async_deps_map: Option<&HashMap<String, Vec<ModuleId>>>,
421  ) -> String {
422    match async_deps_map {
423      Some(map) => format!("var asyncDepsMap = {};", json_stringify_pretty(map)),
424      None => String::new(),
425    }
426  }
427
428  fn get_return_module_object_source(
429    &self,
430    fake_map: &FakeMapValue,
431    async_module: bool,
432    async_deps: Option<String>,
433    fake_map_data_expr: &str,
434    runtime_template: &mut ModuleCodeTemplate,
435  ) -> String {
436    let source = if let FakeMapValue::Bit(bit) = fake_map {
437      if *bit == FakeNamespaceObjectMode::NAMESPACE {
438        format!(
439          "{}(id)",
440          runtime_template.render_runtime_globals(&RuntimeGlobals::REQUIRE)
441        )
442      } else {
443        Self::render_create_fake_namespace_object(
444          runtime_template,
445          format!("{bit}{}", if async_module { " | 16" } else { "" }),
446        )
447      }
448    } else {
449      Self::render_create_fake_namespace_object(
450        runtime_template,
451        format!(
452          "{fake_map_data_expr}{}",
453          if async_module { " | 16" } else { "" }
454        ),
455      )
456    };
457
458    if let Some(async_deps) = async_deps {
459      if !async_module {
460        panic!("Must be async when module is deferred");
461      }
462      let mode = if let FakeMapValue::Bit(bit) = fake_map {
463        Cow::Owned(bit.bits().to_string())
464      } else {
465        fake_map_data_expr.into()
466      };
467      let make_deferred =
468        runtime_template.render_runtime_globals(&RuntimeGlobals::MAKE_DEFERRED_NAMESPACE_OBJECT);
469      let async_transitive = runtime_template
470        .render_runtime_globals(&RuntimeGlobals::DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES);
471      let require = runtime_template.render_runtime_globals(&RuntimeGlobals::REQUIRE);
472      return format!(
473        "{async_deps} ? {async_deps}.length ? {async_transitive}({async_deps}).then({make_deferred}.bind({require}, id, {mode} ^ 1 | 16)) : {make_deferred}(id, {mode} ^ 1 | 16) : {source}"
474      );
475    }
476
477    source
478  }
479
480  fn render_create_fake_namespace_object(
481    runtime_template: &mut ModuleCodeTemplate,
482    mode: String,
483  ) -> String {
484    let create_fake_namespace_object =
485      runtime_template.render_runtime_globals(&RuntimeGlobals::CREATE_FAKE_NAMESPACE_OBJECT);
486    if runtime_template.render_mode() == RuntimeGlobalsRenderMode::RspackExport {
487      let require = runtime_template.render_runtime_globals(&RuntimeGlobals::REQUIRE);
488      format!("{create_fake_namespace_object}.call({require}, id, {mode})")
489    } else {
490      format!("{create_fake_namespace_object}(id, {mode})")
491    }
492  }
493
494  fn get_user_request_map<'a>(
495    &self,
496    dependencies: impl IntoIterator<Item = &'a DependencyId>,
497    compilation: &Compilation,
498  ) -> FxIndexMap<String, Option<String>> {
499    let module_graph = compilation.get_module_graph();
500    let dependencies = dependencies.into_iter();
501    dependencies
502      .filter_map(|dep_id| {
503        let dependency = module_graph.dependency_by_id(dep_id);
504        let dep = if let Some(d) = dependency.as_module_dependency() {
505          Some(d.user_request().to_string())
506        } else {
507          dependency
508            .as_context_dependency()
509            .map(|d| d.request().to_string())
510        };
511        let module_id = module_graph
512          .module_identifier_by_dependency_id(dep_id)
513          .and_then(|module| ChunkGraph::get_module_id(&compilation.module_ids_artifact, *module))
514          .map(|s| s.to_string());
515        // module_id could be None in weak mode
516        dep.map(|dep| (dep, module_id))
517      })
518      .sorted_by(|(a, _), (b, _)| a.cmp(b))
519      .collect()
520  }
521
522  fn get_source_for_empty_async_context(
523    &self,
524    compilation: &Compilation,
525    runtime_template: &mut ModuleCodeTemplate,
526  ) -> String {
527    formatdoc! {r#"
528      function __rspack_empty_async_context(req) {{
529        // Here Promise.resolve().then() is used instead of new Promise() to prevent
530        // uncaught exception popping up in devtools
531        return Promise.resolve().then(function() {{
532          var e = new Error("Cannot find module '" + req + "'");
533          e.code = 'MODULE_NOT_FOUND';
534          throw e;
535        }});
536      }}
537      __rspack_empty_async_context.keys = {keys};
538      __rspack_empty_async_context.resolve = __rspack_empty_async_context;
539      __rspack_empty_async_context.id = {id};
540      {module}.exports = __rspack_empty_async_context;
541      "#,
542      module = runtime_template.render_module_argument(ModuleArgument::Module),
543      keys = runtime_template.returning_function("[]", ""),
544      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
545    }
546  }
547
548  fn get_source_for_empty_context(
549    &self,
550    compilation: &Compilation,
551    runtime_template: &mut ModuleCodeTemplate,
552  ) -> String {
553    formatdoc! {r#"
554      function __rspack_empty_context(req) {{
555        var e = new Error("Cannot find module '" + req + "'");
556        e.code = 'MODULE_NOT_FOUND';
557        throw e;
558      }}
559      __rspack_empty_context.keys = {keys};
560      __rspack_empty_context.resolve = __rspack_empty_context;
561      __rspack_empty_context.id = {id};
562      {module}.exports = __rspack_empty_context;
563      "#,
564      module = runtime_template.render_module_argument(ModuleArgument::Module),
565      keys = runtime_template.returning_function("[]", ""),
566      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
567    }
568  }
569
570  fn get_empty_object_export_source(&self, runtime_template: &mut ModuleCodeTemplate) -> String {
571    concat_string!(
572      "\n",
573      runtime_template.render_module_argument(ModuleArgument::Module),
574      ".exports = {};\n"
575    )
576  }
577
578  fn append_glob_map_entry(entries: &mut String, index: usize, user_request: &str, value: &str) {
579    if index > 0 {
580      entries.push_str(",\n");
581    }
582    entries.push_str("\n  ");
583    entries.push_str(&json_stringify(user_request));
584    entries.push_str(": ");
585    entries.push_str(value);
586  }
587
588  fn get_glob_object_export_source(
589    &self,
590    runtime_template: &mut ModuleCodeTemplate,
591    entries: String,
592  ) -> String {
593    concat_string!(
594      "\n",
595      runtime_template.render_module_argument(ModuleArgument::Module),
596      ".exports = {\n",
597      entries,
598      "\n};\n"
599    )
600  }
601
602  fn get_glob_import_property_access(&self) -> Option<String> {
603    self
604      .options
605      .context_options
606      .glob_import
607      .as_deref()
608      .filter(|import| *import != "*")
609      .map(|import| property_access([import], 0))
610  }
611
612  fn get_sorted_context_block_info(&self, compilation: &Compilation) -> Vec<ContextBlockInfo> {
613    let module_graph = compilation.get_module_graph();
614    let mut block_info: Vec<_> = self
615      .get_blocks()
616      .iter()
617      .filter_map(|b| {
618        let block = module_graph.block_by_id(b)?;
619        let dep = block.get_dependencies().first()?;
620        let dependency = module_graph.dependency_by_id(dep);
621        let user_request = dependency
622          .as_module_dependency()
623          .map(|d| d.user_request().to_string())
624          .or_else(|| {
625            dependency
626              .as_context_dependency()
627              .map(|d| d.request().to_string())
628          })?;
629        Some(ContextBlockInfo {
630          user_request,
631          dep_id: *dep,
632          block_id: block.identifier(),
633        })
634      })
635      .collect();
636    block_info.sort_unstable_by(|a, b| a.user_request.cmp(&b.user_request));
637    block_info
638  }
639
640  fn get_glob_source(
641    &self,
642    compilation: &Compilation,
643    runtime_template: &mut ModuleCodeTemplate,
644  ) -> String {
645    match &self.options.context_options.mode {
646      ContextMode::Lazy => {
647        if self.get_blocks().is_empty() {
648          return self.get_empty_object_export_source(runtime_template);
649        }
650        let block_info = self.get_sorted_context_block_info(compilation);
651
652        let mut entries = String::with_capacity(block_info.len() * 96);
653        for (i, info) in block_info.iter().enumerate() {
654          let mut import_promise = runtime_template.module_namespace_promise(
655            compilation,
656            self.identifier,
657            &info.dep_id,
658            Some(&info.block_id),
659            &info.user_request,
660            DependencyCategory::Esm.as_str(),
661            false,
662            self.options.context_options.phase.unwrap_or_default(),
663          );
664          if let Some(access) = self.get_glob_import_property_access() {
665            import_promise = concat_string!(
666              import_promise,
667              ".then(function(m) { return m",
668              access,
669              "; })"
670            );
671          }
672          Self::append_glob_map_entry(
673            &mut entries,
674            i,
675            &info.user_request,
676            &concat_string!("function() { return ", import_promise, "; }"),
677          );
678        }
679
680        self.get_glob_object_export_source(runtime_template, entries)
681      }
682      _ => {
683        if self.get_dependencies().is_empty() {
684          return self.get_empty_object_export_source(runtime_template);
685        }
686        let dependencies = self.get_dependencies();
687        let map = self.get_user_request_map(dependencies, compilation);
688        let fake_map = self.get_fake_map(dependencies, compilation);
689        let return_module_object = self.get_return_module_object_source(
690          &fake_map,
691          false,
692          None,
693          "fakeMap[id]",
694          runtime_template,
695        );
696
697        let mut entries = String::with_capacity(map.len() * 64);
698        let glob_import_access = self.get_glob_import_property_access();
699        for (i, (user_request, module_id)) in map.iter().enumerate() {
700          let module_id_expr = if let Some(id) = module_id {
701            let mut module_expr = format!(
702              "function() {{ var id = {}; return {return_module_object}; }}()",
703              json_stringify(id)
704            );
705            if let Some(access) = &glob_import_access {
706              module_expr.push_str(access);
707            }
708            module_expr
709          } else {
710            concat_string!("undefined /* ", json_stringify(user_request), " */")
711          };
712          Self::append_glob_map_entry(&mut entries, i, user_request, &module_id_expr);
713        }
714
715        concat_string!(
716          "\n",
717          self.get_fake_map_init_statement(&fake_map),
718          "\n",
719          runtime_template.render_module_argument(ModuleArgument::Module),
720          ".exports = {\n",
721          entries,
722          "\n};\n"
723        )
724      }
725    }
726  }
727
728  #[inline]
729  fn get_source_string(
730    &self,
731    compilation: &Compilation,
732    runtime_template: &mut ModuleCodeTemplate,
733  ) -> String {
734    if let ContextModulePattern::Glob(_) = &self.options.context_options.pattern {
735      return self.get_glob_source(compilation, runtime_template);
736    }
737
738    match self.options.context_options.mode {
739      ContextMode::Lazy => {
740        if !self.get_blocks().is_empty() {
741          self.get_lazy_source(compilation, runtime_template)
742        } else {
743          self.get_source_for_empty_async_context(compilation, runtime_template)
744        }
745      }
746      ContextMode::Eager => {
747        if !self.get_dependencies().is_empty() {
748          self.get_eager_source(compilation, runtime_template)
749        } else {
750          self.get_source_for_empty_async_context(compilation, runtime_template)
751        }
752      }
753      ContextMode::LazyOnce => {
754        if let Some(block) = self.get_blocks().first() {
755          self.get_lazy_once_source(compilation, block, runtime_template)
756        } else {
757          self.get_source_for_empty_async_context(compilation, runtime_template)
758        }
759      }
760      ContextMode::AsyncWeak => {
761        if !self.get_dependencies().is_empty() {
762          self.get_async_weak_source(compilation, runtime_template)
763        } else {
764          self.get_source_for_empty_async_context(compilation, runtime_template)
765        }
766      }
767      ContextMode::Weak => {
768        if !self.get_dependencies().is_empty() {
769          self.get_sync_weak_source(compilation, runtime_template)
770        } else {
771          self.get_source_for_empty_context(compilation, runtime_template)
772        }
773      }
774      ContextMode::Sync => {
775        if !self.get_dependencies().is_empty() {
776          self.get_sync_source(compilation, runtime_template)
777        } else {
778          self.get_source_for_empty_context(compilation, runtime_template)
779        }
780      }
781    }
782  }
783
784  fn get_lazy_source(
785    &self,
786    compilation: &Compilation,
787    runtime_template: &mut ModuleCodeTemplate,
788  ) -> String {
789    let module_graph = compilation.get_module_graph();
790    let blocks = self
791      .get_blocks()
792      .iter()
793      .filter_map(|b| module_graph.block_by_id(b));
794    let block_and_first_dependency_list = blocks
795      .clone()
796      .filter_map(|b| b.get_dependencies().first().map(|d| (b, d)));
797    let first_dependencies = block_and_first_dependency_list.clone().map(|(_, d)| d);
798    let mut has_multiple_or_no_chunks = false;
799    let mut has_no_chunk = true;
800    let mut has_no_module_deferred = true;
801    let fake_map = self.get_fake_map(first_dependencies.clone(), compilation);
802    let has_fake_map = matches!(fake_map, FakeMapValue::Map(_));
803
804    let mut items = block_and_first_dependency_list
805      .filter_map(|(b, d)| {
806        let chunks: Vec<_> = compilation
807          .build_chunk_graph_artifact
808          .chunk_graph
809          .get_block_chunk_group(
810            &b.identifier(),
811            &compilation.build_chunk_graph_artifact.chunk_group_by_ukey,
812          )
813          .expect("should have block chunk group")
814          .chunks
815          .iter()
816          .map(|c| {
817            compilation
818              .build_chunk_graph_artifact
819              .chunk_by_ukey
820              .expect_get(c)
821              .id()
822              .expect("should have chunk id in code generation")
823          })
824          .collect();
825        if !chunks.is_empty() {
826          has_no_chunk = false;
827        }
828        if chunks.len() != 1 {
829          has_multiple_or_no_chunks = true;
830        }
831        let dependency = compilation.get_module_graph().dependency_by_id(d);
832        let user_request = dependency
833          .as_module_dependency()
834          .map(|d| d.user_request().to_string())
835          .or_else(|| {
836            dependency
837              .as_context_dependency()
838              .map(|d| d.request().to_string())
839          })?;
840        let module = module_graph.get_module_by_dependency_id(d)?;
841        let module_id =
842          ChunkGraph::get_module_id(&compilation.module_ids_artifact, module.identifier())?;
843        let async_deps = (self
844          .options
845          .context_options
846          .phase
847          .unwrap_or_default()
848          .is_defer()
849          && !module.build_meta().has_top_level_await())
850        .then(|| {
851          has_no_module_deferred = false;
852          get_outgoing_async_modules(compilation, module.as_ref())
853        });
854        Some((user_request, module_id.to_string(), chunks, async_deps))
855      })
856      .collect::<Vec<_>>();
857    let short_mode = has_no_chunk && has_no_module_deferred && !has_fake_map;
858    items.sort_unstable_by(|a, b| a.0.cmp(&b.0));
859    let map = items
860      .into_iter()
861      .map(|(user_request, module_id, chunks, async_deps)| {
862        let value = if short_mode {
863          serde_json::Value::String(module_id)
864        } else {
865          let mut array = vec![serde_json::json!(module_id)];
866          if let FakeMapValue::Map(fake_map) = &fake_map {
867            array.push(serde_json::json!(fake_map[&module_id].bits()));
868          }
869          if !has_no_chunk {
870            array.push(serde_json::json!(chunks));
871          }
872          if !has_no_module_deferred {
873            array.push(serde_json::json!(async_deps))
874          }
875          serde_json::json!(array)
876        };
877        (user_request, value)
878      })
879      .collect::<HashMap<_, _>>();
880
881    let chunks_position = if has_fake_map { 2 } else { 1 };
882    let async_deps_position = chunks_position + 1;
883    let fetch_priority = match &self.options.context_options.group_options {
884      Some(GroupOptions::ChunkGroup(group)) => group.fetch_priority,
885      _ => None,
886    };
887    let fetch_priority_arg = fetch_priority
888      .map(|x| format!(r#", "{x}""#))
889      .unwrap_or_default();
890    if fetch_priority.is_some() {
891      runtime_template
892        .runtime_requirements_mut()
893        .insert(RuntimeGlobals::HAS_FETCH_PRIORITY);
894    }
895
896    let request_prefix = if has_no_chunk {
897      "Promise.resolve()".to_string()
898    } else if has_multiple_or_no_chunks {
899      let ensure_chunk = runtime_template.render_runtime_globals(&RuntimeGlobals::ENSURE_CHUNK);
900      if fetch_priority.is_some() {
901        format!(
902          r#"Promise.all(ids[{chunks_position}].map(function(chunkId) {{ return {ensure_chunk}(chunkId{fetch_priority_arg}); }}))"#
903        )
904      } else {
905        format!(
906          "Promise.all(ids[{chunks_position}].map(function(chunkId) {{ return {ensure_chunk}(chunkId); }}))"
907        )
908      }
909    } else {
910      let mut chunks_position_buffer = itoa::Buffer::new();
911      let chunks_position_str = chunks_position_buffer.format(chunks_position);
912      format!(
913        "{}(ids[{}][0]{})",
914        runtime_template.render_runtime_globals(&RuntimeGlobals::ENSURE_CHUNK),
915        chunks_position_str,
916        fetch_priority_arg
917      )
918    };
919    let return_module_object = self.get_return_module_object_source(
920      &fake_map,
921      true,
922      if has_no_module_deferred {
923        None
924      } else {
925        Some(format!("ids[{async_deps_position}]"))
926      },
927      if short_mode { "invalid" } else { "ids[1]" },
928      runtime_template,
929    );
930
931    let has_own_property =
932      runtime_template.render_runtime_globals(&RuntimeGlobals::HAS_OWN_PROPERTY);
933    let async_context = if has_no_chunk {
934      let then_function = runtime_template.basic_function(
935        "",
936        &formatdoc! {
937          r#"if(!{has_own_property}(map, req)) {{
938            var e = new Error("Cannot find module '" + req + "'");
939            e.code = 'MODULE_NOT_FOUND';
940            throw e;
941          }}
942
943          {}
944          return {return_module_object};"#,
945          if short_mode {
946            "var id = map[req];"
947          } else {
948            "var ids = map[req], id = ids[0];"
949          }
950        },
951      );
952      formatdoc! {r#"
953        function __rspack_async_context(req) {{
954          return Promise.resolve().then({then_function});
955        }}
956      "#}
957    } else {
958      let then_function = runtime_template.returning_function(&return_module_object, "");
959      let module_not_found = runtime_template.basic_function(
960        "",
961        &formatdoc! {
962          r#"var e = new Error("Cannot find module '" + req + "'");
963            e.code = 'MODULE_NOT_FOUND';
964            throw e;"#
965        },
966      );
967      formatdoc! {r#"
968        function __rspack_async_context(req) {{
969          if(!{}(map, req)) {{
970            return Promise.resolve().then({module_not_found});
971          }}
972
973          var ids = map[req], id = ids[0];
974          return {request_prefix}.then({then_function});
975        }}
976        "#,
977        runtime_template.render_runtime_globals(&RuntimeGlobals::HAS_OWN_PROPERTY),
978      }
979    };
980
981    formatdoc! {r#"
982      var map = {map};
983      {async_context}
984      __rspack_async_context.keys = {keys};
985      __rspack_async_context.id = {id};
986      {module}.exports = __rspack_async_context;
987      "#,
988      module = runtime_template.render_module_argument(ModuleArgument::Module),
989      map = json_stringify_pretty(&map),
990      keys = runtime_template.returning_function("Object.keys(map)", ""),
991      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
992    }
993  }
994
995  fn get_lazy_once_source(
996    &self,
997    compilation: &Compilation,
998    block_id: &AsyncDependenciesBlockIdentifier,
999    runtime_template: &mut ModuleCodeTemplate,
1000  ) -> String {
1001    let mg = compilation.get_module_graph();
1002    let block = mg.block_by_id_expect(block_id);
1003    let dependencies = block.get_dependencies();
1004    let promise = runtime_template.block_promise(Some(block_id), compilation, "lazy-once context");
1005    let map = self.get_user_request_map(dependencies, compilation);
1006    let fake_map = self.get_fake_map(dependencies, compilation);
1007    let async_deps_map = self
1008      .options
1009      .context_options
1010      .phase
1011      .unwrap_or_default()
1012      .is_defer()
1013      .then(|| self.get_module_deferred_async_deps_map(dependencies, compilation));
1014
1015    let return_module_object_source = self.get_return_module_object_source(
1016      &fake_map,
1017      true,
1018      async_deps_map
1019        .is_some()
1020        .then(|| "asyncDepsMap[id]".to_string()),
1021      "fakeMap[id]",
1022      runtime_template,
1023    );
1024    let then_function = runtime_template.returning_function(&return_module_object_source, "id");
1025
1026    let has_own_property =
1027      runtime_template.render_runtime_globals(&RuntimeGlobals::HAS_OWN_PROPERTY);
1028    let module_not_found = runtime_template.basic_function(
1029      "",
1030      &formatdoc! {
1031        r#"if(!{has_own_property}(map, req)) {{
1032          var e = new Error("Cannot find module '" + req + "'");
1033          e.code = 'MODULE_NOT_FOUND';
1034          throw e;
1035        }}
1036        return map[req];"#
1037      },
1038    );
1039
1040    formatdoc! {r#"
1041      var map = {map};
1042      {fake_map_init_statement}
1043      {async_deps_map_init_statement}
1044
1045      function __rspack_async_context(req) {{
1046        return __rspack_async_context_resolve(req).then({then_function});
1047      }}
1048      function __rspack_async_context_resolve(req) {{
1049        return {promise}.then({module_not_found});
1050      }}
1051      __rspack_async_context.keys = {keys};
1052      __rspack_async_context.resolve = __rspack_async_context_resolve;
1053      __rspack_async_context.id = {id};
1054      {module}.exports = __rspack_async_context;
1055      "#,
1056      module = runtime_template.render_module_argument(ModuleArgument::Module),
1057      map = json_stringify_pretty(&map),
1058      fake_map_init_statement = self.get_fake_map_init_statement(&fake_map),
1059      async_deps_map_init_statement = self.get_module_deferred_async_deps_map_init_statement(async_deps_map.as_ref()),
1060      keys = runtime_template.returning_function("Object.keys(map)", ""),
1061      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
1062    }
1063  }
1064
1065  fn get_async_weak_source(
1066    &self,
1067    compilation: &Compilation,
1068    runtime_template: &mut ModuleCodeTemplate,
1069  ) -> String {
1070    let dependencies = self.get_dependencies();
1071    let map = self.get_user_request_map(dependencies, compilation);
1072    let fake_map = self.get_fake_map(dependencies, compilation);
1073    let async_deps_map = self
1074      .options
1075      .context_options
1076      .phase
1077      .unwrap_or_default()
1078      .is_defer()
1079      .then(|| self.get_module_deferred_async_deps_map(dependencies, compilation));
1080
1081    let return_module_object = self.get_return_module_object_source(
1082      &fake_map,
1083      true,
1084      async_deps_map
1085        .is_some()
1086        .then(|| "asyncDepsMap[id]".to_string()),
1087      "fakeMap[id]",
1088      runtime_template,
1089    );
1090    let module_factories =
1091      runtime_template.render_runtime_globals(&RuntimeGlobals::MODULE_FACTORIES);
1092    let then_function = runtime_template.basic_function(
1093      "id",
1094      &formatdoc! {
1095        r#"if(!{module_factories}[id]) {{
1096          var e = new Error("Module '" + req + "' ('" + id + "') is not available (weak dependency)");
1097          e.code = 'MODULE_NOT_FOUND';
1098          throw e;
1099        }}
1100        return {return_module_object};"#
1101      },
1102    );
1103    let has_own_property =
1104      runtime_template.render_runtime_globals(&RuntimeGlobals::HAS_OWN_PROPERTY);
1105    let module_not_found = runtime_template.basic_function(
1106      "",
1107      &formatdoc! {
1108        r#"if(!{has_own_property}(map, req)) {{
1109          var e = new Error("Cannot find module '" + req + "'");
1110          e.code = 'MODULE_NOT_FOUND';
1111          throw e;
1112        }}
1113        return map[req];"#
1114      },
1115    );
1116
1117    formatdoc! {r#"
1118      var map = {map};
1119      {fake_map_init_statement}
1120      {async_deps_map_init_statement}
1121
1122      function __rspack_async_context(req) {{
1123        return __rspack_async_context_resolve(req).then({then_function});
1124      }}
1125      function __rspack_async_context_resolve(req) {{
1126        // Here Promise.resolve().then() is used instead of new Promise() to prevent
1127        // uncaught exception popping up in devtools
1128        return Promise.resolve().then({module_not_found});
1129      }}
1130      __rspack_async_context.keys = {keys};
1131      __rspack_async_context.resolve = __rspack_async_context_resolve;
1132      __rspack_async_context.id = {id};
1133      {module}.exports = __rspack_async_context;
1134      "#,
1135      module = runtime_template.render_module_argument(ModuleArgument::Module),
1136      map = json_stringify_pretty(&map),
1137      fake_map_init_statement = self.get_fake_map_init_statement(&fake_map),
1138      async_deps_map_init_statement = self.get_module_deferred_async_deps_map_init_statement(async_deps_map.as_ref()),
1139      keys = runtime_template.returning_function("Object.keys(map)", ""),
1140      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
1141    }
1142  }
1143
1144  fn get_sync_weak_source(
1145    &self,
1146    compilation: &Compilation,
1147    runtime_template: &mut ModuleCodeTemplate,
1148  ) -> String {
1149    let dependencies = self.get_dependencies();
1150    let map = self.get_user_request_map(dependencies, compilation);
1151    let fake_map = self.get_fake_map(dependencies, compilation);
1152    let return_module_object =
1153      self.get_return_module_object_source(&fake_map, true, None, "fakeMap[id]", runtime_template);
1154    formatdoc! {r#"
1155      var map = {map};
1156      {fake_map_init_statement}
1157
1158      function __rspack_context_module(req) {{
1159        var id = __rspack_context_module_resolve(req);
1160        if(!{module_factories}[id]) {{
1161          var e = new Error("Module '" + req + "' ('" + id + "') is not available (weak dependency)");
1162          e.code = 'MODULE_NOT_FOUND';
1163          throw e;
1164        }}
1165        return {return_module_object};
1166      }}
1167      function __rspack_context_module_resolve(req) {{
1168        if(!{has_own_property}(map, req)) {{
1169          var e = new Error("Cannot find module '" + req + "'");
1170          e.code = 'MODULE_NOT_FOUND';
1171          throw e;
1172        }}
1173        return map[req];
1174      }}
1175      __rspack_context_module.keys = {keys};
1176      __rspack_context_module.resolve = __rspack_context_module_resolve;
1177      __rspack_context_module.id = {id};
1178      {module}.exports = __rspack_context_module;
1179      "#,
1180      module = runtime_template.render_module_argument(ModuleArgument::Module),
1181      map = json_stringify_pretty(&map),
1182      fake_map_init_statement = self.get_fake_map_init_statement(&fake_map),
1183      module_factories = runtime_template.render_runtime_globals(&RuntimeGlobals::MODULE_FACTORIES),
1184      has_own_property = runtime_template.render_runtime_globals(&RuntimeGlobals::HAS_OWN_PROPERTY),
1185      keys = runtime_template.returning_function("Object.keys(map)", ""),
1186      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
1187    }
1188  }
1189
1190  fn get_eager_source(
1191    &self,
1192    compilation: &Compilation,
1193    runtime_template: &mut ModuleCodeTemplate,
1194  ) -> String {
1195    let dependencies = self.get_dependencies();
1196    let map = self.get_user_request_map(dependencies, compilation);
1197    let fake_map = self.get_fake_map(dependencies, compilation);
1198    let async_deps_map = self
1199      .options
1200      .context_options
1201      .phase
1202      .unwrap_or_default()
1203      .is_defer()
1204      .then(|| self.get_module_deferred_async_deps_map(dependencies, compilation));
1205    let return_module_object_source = self.get_return_module_object_source(
1206      &fake_map,
1207      true,
1208      async_deps_map
1209        .is_some()
1210        .then(|| "asyncDepsMap[id]".to_string()),
1211      "fakeMap[id]",
1212      runtime_template,
1213    );
1214    let then_function = runtime_template.returning_function(&return_module_object_source, "id");
1215    let has_own_property =
1216      runtime_template.render_runtime_globals(&RuntimeGlobals::HAS_OWN_PROPERTY);
1217    let module_not_found = runtime_template.basic_function(
1218      "",
1219      &formatdoc! {
1220        r#"if(!{has_own_property}(map, req)) {{
1221          var e = new Error("Cannot find module '" + req + "'");
1222          e.code = 'MODULE_NOT_FOUND';
1223          throw e;
1224        }}
1225        return map[req];"#
1226      },
1227    );
1228
1229    formatdoc! {r#"
1230      var map = {map};
1231      {fake_map_init_statement}
1232      {async_deps_map_init_statement}
1233
1234      function __rspack_async_context(req) {{
1235        return __rspack_async_context_resolve(req).then({then_function});
1236      }}
1237      function __rspack_async_context_resolve(req) {{
1238        // Here Promise.resolve().then() is used instead of new Promise() to prevent
1239        // uncaught exception popping up in devtools
1240        return Promise.resolve().then({module_not_found});
1241      }}
1242      __rspack_async_context.keys = {keys};
1243      __rspack_async_context.resolve = __rspack_async_context_resolve;
1244      __rspack_async_context.id = {id};
1245      {module}.exports = __rspack_async_context;
1246      "#,
1247      module = runtime_template.render_module_argument(ModuleArgument::Module),
1248      map = json_stringify_pretty(&map),
1249      fake_map_init_statement = self.get_fake_map_init_statement(&fake_map),
1250      async_deps_map_init_statement = self.get_module_deferred_async_deps_map_init_statement(async_deps_map.as_ref()),
1251      keys = runtime_template.returning_function("Object.keys(map)", ""),
1252      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
1253    }
1254  }
1255
1256  fn get_sync_source(
1257    &self,
1258    compilation: &Compilation,
1259    runtime_template: &mut ModuleCodeTemplate,
1260  ) -> String {
1261    let dependencies = self.get_dependencies();
1262    let map = self.get_user_request_map(dependencies, compilation);
1263    let fake_map = self.get_fake_map(dependencies, compilation);
1264    let return_module_object =
1265      self.get_return_module_object_source(&fake_map, false, None, "fakeMap[id]", runtime_template);
1266    formatdoc! {r#"
1267      var map = {map};
1268      {fake_map_init_statement}
1269
1270      function __rspack_context_module(req) {{
1271        var id = __rspack_context_module_resolve(req);
1272        return {return_module_object};
1273      }}
1274      function __rspack_context_module_resolve(req) {{
1275        if(!{has_own_property}(map, req)) {{
1276          var e = new Error("Cannot find module '" + req + "'");
1277          e.code = 'MODULE_NOT_FOUND';
1278          throw e;
1279        }}
1280        return map[req];
1281      }}
1282      __rspack_context_module.keys = {keys};
1283      __rspack_context_module.resolve = __rspack_context_module_resolve;
1284      {module}.exports = __rspack_context_module;
1285      __rspack_context_module.id = {id};
1286      "#,
1287      module = runtime_template.render_module_argument(ModuleArgument::Module),
1288      map = json_stringify_pretty(&map),
1289      fake_map_init_statement = self.get_fake_map_init_statement(&fake_map),
1290      has_own_property = runtime_template.render_runtime_globals(&RuntimeGlobals::HAS_OWN_PROPERTY),
1291      keys = runtime_template.returning_function("Object.keys(map)", ""),
1292      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
1293    }
1294  }
1295
1296  fn get_source(&self, source_string: String, compilation: &Compilation) -> BoxSource {
1297    let source_map_kind = self.get_source_map_kind();
1298    if source_map_kind.enabled() {
1299      let source_protocol = compilation.options.experiments.runtime_mode.to_string();
1300      OriginalSource::new(
1301        source_string,
1302        format!(
1303          "{source_protocol}://{}",
1304          make_paths_relative(&compilation.options.context, self.identifier.as_str(),)
1305        ),
1306      )
1307      .boxed()
1308    } else {
1309      RawStringSource::from(source_string).boxed()
1310    }
1311  }
1312}
1313
1314impl DependenciesBlock for ContextModule {
1315  fn add_block_id(&mut self, block: AsyncDependenciesBlockIdentifier) {
1316    self.blocks.push(block)
1317  }
1318
1319  fn get_blocks(&self) -> &[AsyncDependenciesBlockIdentifier] {
1320    &self.blocks
1321  }
1322
1323  fn add_dependency_id(&mut self, dependency: DependencyId) {
1324    self.dependencies.push(dependency)
1325  }
1326
1327  fn remove_dependency_id(&mut self, dependency: DependencyId) {
1328    self.dependencies.retain(|d| d != &dependency)
1329  }
1330
1331  fn get_dependencies(&self) -> &[DependencyId] {
1332    &self.dependencies
1333  }
1334}
1335
1336#[cacheable_dyn]
1337#[async_trait::async_trait]
1338impl Module for ContextModule {
1339  impl_module_meta_info!();
1340
1341  fn module_type(&self) -> &ModuleType {
1342    &ModuleType::JsAuto
1343  }
1344
1345  fn source_types(&self, _module_graph: &ModuleGraph) -> &[SourceType] {
1346    &[SourceType::JavaScript]
1347  }
1348
1349  fn source(&self) -> Option<&rspack_sources::BoxSource> {
1350    None
1351  }
1352
1353  fn readable_identifier(&self, context: &crate::Context) -> std::borrow::Cow<'_, str> {
1354    let identifier = contextify(
1355      context,
1356      if self.options.resource.as_str().is_empty() {
1357        "false"
1358      } else {
1359        self.options.resource.as_str()
1360      },
1361    );
1362    create_identifier(&self.options, Some(identifier.as_str()))
1363      .to_string()
1364      .into()
1365  }
1366
1367  fn size(
1368    &self,
1369    _source_type: Option<&crate::SourceType>,
1370    _compilation: Option<&Compilation>,
1371  ) -> f64 {
1372    160.0
1373  }
1374
1375  fn lib_ident(&self, options: LibIdentOptions) -> Option<Cow<'_, str>> {
1376    let mut id = String::new();
1377    if let Some(layer) = &self.options.layer {
1378      id += "(";
1379      id += layer;
1380      id += ")/";
1381    }
1382    id += &contextify(
1383      options.context,
1384      if self.options.resource.as_str().is_empty() {
1385        "false"
1386      } else {
1387        self.options.resource.as_str()
1388      },
1389    );
1390    id += " ";
1391    id += self.options.context_options.mode.as_str();
1392    if self.options.context_options.recursive {
1393      id += " recursive";
1394    }
1395    if !self.options.addon.is_empty() {
1396      id += " ";
1397      id += &self.options.addon;
1398    }
1399    if let Some(regexp) = self.options.context_options.pattern.to_pretty_string(true) {
1400      id += " ";
1401      id += &regexp;
1402    }
1403    if let Some(include) = &self.options.context_options.include {
1404      id += " include: ";
1405      id += &include.to_pretty_string(true);
1406    }
1407    if let Some(exclude) = &self.options.context_options.exclude {
1408      id += " exclude: ";
1409      id += &exclude.to_pretty_string(true);
1410    }
1411    if let Some(specifiers) = &self.options.context_options.referenced_specifiers {
1412      id += " referencedExports: ";
1413      id += &specifiers
1414        .iter()
1415        .map(|specifier| {
1416          let s = specifier.names.iter().join(".");
1417          if specifier.namespace_object_as_context && specifier.is_call {
1418            format!("*.{s}()")
1419          } else {
1420            s
1421          }
1422        })
1423        .join(", ");
1424    }
1425    if let Some(import) = &self.options.context_options.glob_import {
1426      id += " globImport: ";
1427      id += import;
1428    }
1429    if self.options.context_options.glob_exhaustive {
1430      id += " globExhaustive";
1431    }
1432    append_context_identifier(&mut id, &self.options.context_options, " context: ");
1433    if !self.options.context_options.glob_case_sensitive {
1434      id += " globCaseInsensitive";
1435    }
1436    Some(Cow::Owned(id))
1437  }
1438
1439  async fn build(
1440    mut self: Box<Self>,
1441    _build_context: BuildContext,
1442    _: Option<&Compilation>,
1443  ) -> Result<BuildResult> {
1444    let resolve_dependencies = &self.resolve_dependencies;
1445    let context_element_dependencies = resolve_dependencies(self.options.clone()).await?;
1446
1447    let mut dependencies: Vec<BoxDependency> = vec![];
1448    let mut blocks = vec![];
1449    if matches!(self.options.context_options.mode, ContextMode::LazyOnce)
1450      && !context_element_dependencies.is_empty()
1451    {
1452      let loc = DependencyLocation::Real(RealDependencyLocation::new(
1453        (
1454          self.options.context_options.start,
1455          self.options.context_options.end,
1456        )
1457          .into(),
1458        None,
1459      ));
1460      let mut block = AsyncDependenciesBlock::new(
1461        (*self.identifier).into(),
1462        Some(loc),
1463        None,
1464        context_element_dependencies
1465          .into_iter()
1466          .map(BoxDependency::new)
1467          .collect(),
1468        None,
1469      );
1470      if let Some(group_options) = &self.options.context_options.group_options {
1471        block.set_group_options(group_options.clone());
1472      }
1473      blocks.push(Box::new(block));
1474    } else if matches!(self.options.context_options.mode, ContextMode::Lazy) {
1475      let mut index = 0;
1476      for context_element_dependency in context_element_dependencies {
1477        let group_options = self
1478          .options
1479          .context_options
1480          .group_options
1481          .as_ref()
1482          .and_then(|g| g.normal_options());
1483        let name = group_options
1484          .and_then(|group_options| group_options.name.as_ref())
1485          .map(|name| {
1486            let name = if !(name.contains(CHUNK_NAME_INDEX_PLACEHOLDER)
1487              || name.contains(CHUNK_NAME_REQUEST_PLACEHOLDER))
1488            {
1489              Cow::Owned(format!("{name}{CHUNK_NAME_INDEX_PLACEHOLDER}"))
1490            } else {
1491              Cow::Borrowed(name)
1492            };
1493
1494            let name = name.cow_replace(CHUNK_NAME_INDEX_PLACEHOLDER, &index.to_string());
1495            let name = name.cow_replace(
1496              CHUNK_NAME_REQUEST_PLACEHOLDER,
1497              &to_path(&context_element_dependency.user_request),
1498            );
1499
1500            index += 1;
1501            name.into_owned()
1502          });
1503        let preload_order = group_options.and_then(|o| o.preload_order);
1504        let prefetch_order = group_options.and_then(|o| o.prefetch_order);
1505        let fetch_priority = group_options.and_then(|o| o.fetch_priority);
1506        let mut block = AsyncDependenciesBlock::new(
1507          (*self.identifier).into(),
1508          None,
1509          Some(&context_element_dependency.user_request.clone()),
1510          vec![BoxDependency::new(context_element_dependency)],
1511          Some(self.options.context_options.request.clone()),
1512        );
1513        block.set_group_options(GroupOptions::ChunkGroup(ChunkGroupOptions::new(
1514          name,
1515          preload_order,
1516          prefetch_order,
1517          fetch_priority,
1518        )));
1519        blocks.push(Box::new(block));
1520      }
1521    } else {
1522      dependencies = context_element_dependencies
1523        .into_iter()
1524        .map(BoxDependency::new)
1525        .collect();
1526    }
1527
1528    if !self.options.resource.as_str().is_empty() {
1529      let mut context_dependencies: InternedPathSet = Default::default();
1530      context_dependencies.insert(self.options.resource.as_std_path().into());
1531      self.build_info.dependencies.context = context_dependencies;
1532    }
1533
1534    Ok(BuildResult {
1535      module: BoxModule::new(self),
1536      dependencies,
1537      blocks,
1538      optimization_bailouts: vec![],
1539    })
1540  }
1541
1542  // #[tracing::instrument("ContextModule::code_generation", skip_all, fields(identifier = ?self.identifier()))]
1543  async fn code_generation(
1544    &self,
1545    code_generation_context: &mut ModuleCodeGenerationContext,
1546  ) -> Result<CodeGenerationResultBuilder> {
1547    let ModuleCodeGenerationContext {
1548      compilation,
1549      runtime_template,
1550      ..
1551    } = code_generation_context;
1552    let mut code_generation_result = CodeGenerationResultBuilder::default();
1553    let source = self.get_source(
1554      self.get_source_string(compilation, runtime_template),
1555      compilation,
1556    );
1557    code_generation_result.add(SourceType::JavaScript, source);
1558    let mut all_deps = self.get_dependencies().to_vec();
1559    let module_graph = compilation.get_module_graph();
1560    for block in self.get_blocks() {
1561      let block = module_graph
1562        .block_by_id(block)
1563        .expect("should have block in ContextModule code_generation");
1564      all_deps.extend(block.get_dependencies());
1565    }
1566
1567    Ok(code_generation_result)
1568  }
1569
1570  async fn get_runtime_hash(
1571    &self,
1572    compilation: &Compilation,
1573    runtime: Option<&RuntimeSpec>,
1574  ) -> Result<RspackHashDigest> {
1575    let mut hasher = RspackHasher::from(&compilation.options.output);
1576    module_update_hash(self, &mut hasher, compilation, runtime);
1577    Ok(hasher.digest(&compilation.options.output.hash_digest))
1578  }
1579}
1580
1581impl_empty_diagnosable_trait!(ContextModule);
1582
1583impl Identifiable for ContextModule {
1584  fn identifier(&self) -> Identifier {
1585    self.identifier
1586  }
1587}
1588
1589fn create_identifier(options: &ContextModuleOptions, resource: Option<&str>) -> Identifier {
1590  let mut id = resource
1591    .unwrap_or(if options.resource.as_str().is_empty() {
1592      "false"
1593    } else {
1594      options.resource.as_str()
1595    })
1596    .to_owned();
1597  if !options.resource_query.is_empty() {
1598    id += "|";
1599    id += &options.resource_query;
1600  }
1601  if !options.resource_fragment.is_empty() {
1602    id += "|";
1603    id += &options.resource_fragment;
1604  }
1605  id += "|";
1606  id += options.context_options.mode.as_str();
1607  if !options.context_options.recursive {
1608    id += "|nonrecursive";
1609  }
1610  if !options.addon.is_empty() {
1611    id += "|";
1612    id += &options.addon;
1613  }
1614  if let Some(pattern) = options.context_options.pattern.to_pretty_string(false) {
1615    id += "|";
1616    id += &pattern;
1617  }
1618  if let Some(include) = &options.context_options.include {
1619    id += "|include: ";
1620    id += &include.to_source_string();
1621  }
1622  if let Some(exclude) = &options.context_options.exclude {
1623    id += "|exclude: ";
1624    id += &exclude.to_source_string();
1625  }
1626  if let Some(specifiers) = &options.context_options.referenced_specifiers {
1627    id += "|referencedExports: ";
1628    id += &specifiers
1629      .iter()
1630      .map(|specifier| {
1631        if specifier.namespace_object_as_context && specifier.is_call {
1632          format!("*.{}()", specifier.names.iter().join("."))
1633        } else {
1634          specifier.names.iter().join(".")
1635        }
1636      })
1637      .join(", ");
1638  }
1639  if let Some(import) = &options.context_options.glob_import {
1640    id += "|globImport: ";
1641    id += import;
1642  }
1643  if options.context_options.glob_exhaustive {
1644    id += "|globExhaustive";
1645  }
1646  append_context_identifier(&mut id, &options.context_options, "|context: ");
1647  if !options.context_options.glob_case_sensitive {
1648    id += "|globCaseInsensitive";
1649  }
1650
1651  if let Some(GroupOptions::ChunkGroup(group)) = &options.context_options.group_options {
1652    if let Some(chunk_name) = &group.name {
1653      id += "|chunkName: ";
1654      id += chunk_name;
1655    }
1656    id += "|groupOptions: {";
1657    if let Some(o) = group.prefetch_order {
1658      write!(id, "prefetchOrder: {o},").expect("infallible write to String");
1659    }
1660    if let Some(o) = group.preload_order {
1661      write!(id, "preloadOrder: {o},").expect("infallible write to String");
1662    }
1663    if let Some(o) = group.fetch_priority {
1664      write!(id, "fetchPriority: {o},").expect("infallible write to String");
1665    }
1666    id += "}";
1667  }
1668  id += match options.context_options.namespace_object {
1669    ContextNameSpaceObject::Strict => "|strict namespace object",
1670    ContextNameSpaceObject::Bool(true) => "|namespace object",
1671    _ => "",
1672  };
1673  if let Some(attributes) = &options.context_options.attributes {
1674    id += "|importAttributes: ";
1675    id += &simd_json::to_string(attributes).expect("json stringify failed");
1676  }
1677  if let Some(phase) = &options.context_options.phase {
1678    id += "|importPhase: ";
1679    id += phase.as_str();
1680  }
1681  if let Some(layer) = &options.layer {
1682    id += "|layer: ";
1683    id += layer;
1684  }
1685  id.into()
1686}
1687
1688fn append_context_identifier(id: &mut String, options: &ContextOptions, prefix: &str) {
1689  if !matches!(options.pattern, ContextModulePattern::Glob(_)) {
1690    return;
1691  }
1692  let Some(context) = context_identifier(&options.compiler_context, &options.context) else {
1693    return;
1694  };
1695  id.push_str(prefix);
1696  id.push_str(&context);
1697}