Skip to main content

rspack_core/
context_module.rs

1use std::{borrow::Cow, fmt::Write, hash::Hash, sync::Arc};
2
3use cow_utils::CowUtils;
4use derive_more::Debug;
5use futures::future::BoxFuture;
6use indoc::formatdoc;
7use itertools::Itertools;
8use rspack_cacheable::{
9  cacheable, cacheable_dyn,
10  with::{AsCacheable, AsOption, AsPreset, AsVec, Unsupported},
11};
12use rspack_collections::{Identifiable, Identifier};
13use rspack_error::{Result, impl_empty_diagnosable_trait};
14use rspack_hash::{RspackHash, RspackHashDigest};
15use rspack_macros::impl_source_map_config;
16use rspack_paths::{ArcPathSet, Utf8PathBuf};
17use rspack_regex::RspackRegex;
18use rspack_sources::{BoxSource, OriginalSource, RawStringSource, SourceExt};
19use rspack_util::{
20  fx_hash::FxIndexMap,
21  identifier::make_paths_relative,
22  itoa, json_stringify, json_stringify_pretty,
23  source_map::{ModuleSourceMapConfig, SourceMapKind},
24};
25use rustc_hash::FxHashMap as HashMap;
26
27use crate::{
28  AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, BoxDependency, BoxModule, BuildContext,
29  BuildInfo, BuildMeta, BuildMetaDefaultObject, BuildMetaExportsType, BuildResult, ChunkGraph,
30  ChunkGroupOptions, CodeGenerationResult, Compilation, ContextElementDependency,
31  DependenciesBlock, Dependency, DependencyCategory, DependencyId, DependencyLocation,
32  DynamicImportMode, ExportsType, FactoryMeta, FakeNamespaceObjectMode, GroupOptions,
33  ImportAttributes, ImportPhase, LibIdentOptions, Module, ModuleArgument,
34  ModuleCodeGenerationContext, ModuleCodeTemplate, ModuleGraph, ModuleId, ModuleIdsArtifact,
35  ModuleLayer, ModuleType, RealDependencyLocation, ReferencedSpecifier, Resolve, RuntimeGlobals,
36  RuntimeSpec, SourceType, contextify, get_exports_type_with_strict, get_outgoing_async_modules,
37  impl_module_meta_info, module_update_hash, to_path,
38};
39
40static CHUNK_NAME_INDEX_PLACEHOLDER: &str = "[index]";
41static CHUNK_NAME_REQUEST_PLACEHOLDER: &str = "[request]";
42
43#[cacheable]
44#[derive(Debug, PartialEq, Eq, Clone, Hash)]
45pub enum ContextMode {
46  Sync,
47  Eager,
48  Weak,
49  AsyncWeak,
50  Lazy,
51  LazyOnce,
52}
53
54impl ContextMode {
55  pub fn as_str(&self) -> &str {
56    match self {
57      ContextMode::Sync => "sync",
58      ContextMode::Eager => "eager",
59      ContextMode::Weak => "weak",
60      ContextMode::Lazy => "lazy",
61      ContextMode::LazyOnce => "lazy-once",
62      ContextMode::AsyncWeak => "async-weak",
63    }
64  }
65}
66
67impl From<&str> for ContextMode {
68  fn from(value: &str) -> Self {
69    match try_convert_str_to_context_mode(value) {
70      Some(m) => m,
71      // TODO should give warning
72      _ => panic!("unknown context mode"),
73    }
74  }
75}
76
77impl From<DynamicImportMode> for ContextMode {
78  fn from(value: DynamicImportMode) -> Self {
79    match value {
80      DynamicImportMode::Lazy => Self::Lazy,
81      DynamicImportMode::Weak => Self::AsyncWeak,
82      DynamicImportMode::Eager => Self::Eager,
83      DynamicImportMode::LazyOnce => Self::LazyOnce,
84    }
85  }
86}
87
88pub fn try_convert_str_to_context_mode(s: &str) -> Option<ContextMode> {
89  match s {
90    "sync" => Some(ContextMode::Sync),
91    "eager" => Some(ContextMode::Eager),
92    "weak" => Some(ContextMode::Weak),
93    "lazy" => Some(ContextMode::Lazy),
94    "lazy-once" => Some(ContextMode::LazyOnce),
95    "async-weak" => Some(ContextMode::AsyncWeak),
96    // TODO should give warning
97    _ => None,
98  }
99}
100
101#[cacheable]
102#[derive(Debug, Clone, PartialEq, Eq, Hash)]
103pub enum ContextNameSpaceObject {
104  Bool(bool),
105  Strict,
106  Unset,
107}
108
109impl ContextNameSpaceObject {
110  pub fn is_false(&self) -> bool {
111    matches!(self, ContextNameSpaceObject::Unset)
112      || matches!(self, ContextNameSpaceObject::Bool(v) if !v)
113  }
114}
115
116#[cacheable]
117#[derive(Debug, Clone, Copy, Hash, PartialEq, PartialOrd, Ord, Eq)]
118pub enum ContextTypePrefix {
119  Import,
120  Normal,
121}
122
123#[cacheable]
124#[derive(Debug, Clone)]
125pub struct ContextOptions {
126  pub mode: ContextMode,
127  pub recursive: bool,
128  pub reg_exp: Option<RspackRegex>,
129  pub include: Option<RspackRegex>,
130  pub exclude: Option<RspackRegex>,
131  pub category: DependencyCategory,
132  pub request: String,
133  pub context: String,
134  pub namespace_object: ContextNameSpaceObject,
135  pub group_options: Option<GroupOptions>,
136  pub replaces: Vec<(String, u32, u32)>,
137  pub start: u32,
138  pub end: u32,
139  #[cacheable(with=AsOption<AsVec<AsCacheable>>)]
140  pub referenced_specifiers: Option<Vec<ReferencedSpecifier>>,
141  pub attributes: Option<ImportAttributes>,
142  pub phase: Option<ImportPhase>,
143}
144
145#[cacheable]
146#[derive(Debug, Clone)]
147pub struct ContextModuleOptions {
148  pub addon: String,
149  #[cacheable(with=AsPreset)]
150  pub resource: Utf8PathBuf,
151  pub resource_query: String,
152  pub resource_fragment: String,
153  pub context_options: ContextOptions,
154  pub layer: Option<ModuleLayer>,
155  pub resolve_options: Option<Arc<Resolve>>,
156  pub type_prefix: ContextTypePrefix,
157}
158
159#[derive(Debug)]
160pub enum FakeMapValue {
161  Bit(FakeNamespaceObjectMode),
162  Map(HashMap<String, FakeNamespaceObjectMode>),
163}
164
165pub type ResolveContextModuleDependencies = Arc<
166  dyn Fn(ContextModuleOptions) -> BoxFuture<'static, Result<Vec<ContextElementDependency>>>
167    + Send
168    + Sync,
169>;
170
171#[impl_source_map_config]
172#[cacheable]
173#[derive(Debug)]
174pub struct ContextModule {
175  dependencies: Vec<DependencyId>,
176  blocks: Vec<AsyncDependenciesBlockIdentifier>,
177  identifier: Identifier,
178  options: ContextModuleOptions,
179  factory_meta: Option<FactoryMeta>,
180  build_info: BuildInfo,
181  build_meta: BuildMeta,
182  #[debug(skip)]
183  #[cacheable(with=Unsupported)]
184  resolve_dependencies: ResolveContextModuleDependencies,
185}
186
187impl ContextModule {
188  pub fn new(
189    resolve_dependencies: ResolveContextModuleDependencies,
190    options: ContextModuleOptions,
191  ) -> Self {
192    Self {
193      dependencies: Vec::new(),
194      blocks: Vec::new(),
195      identifier: create_identifier(&options, None),
196      options,
197      factory_meta: None,
198      build_info: Default::default(),
199      build_meta: BuildMeta {
200        exports_type: BuildMetaExportsType::Default,
201        default_object: BuildMetaDefaultObject::RedirectWarn,
202        ..Default::default()
203      },
204      source_map_kind: SourceMapKind::empty(),
205      resolve_dependencies,
206    }
207  }
208
209  fn get_module_id<'a>(&self, module_ids: &'a ModuleIdsArtifact) -> &'a ModuleId {
210    ChunkGraph::get_module_id(module_ids, self.identifier).expect("module id not found")
211  }
212
213  pub fn get_context_options(&self) -> &ContextOptions {
214    &self.options.context_options
215  }
216
217  fn get_fake_map<'a>(
218    &self,
219    dependencies: impl IntoIterator<Item = &'a DependencyId>,
220    compilation: &Compilation,
221  ) -> FakeMapValue {
222    let dependencies = dependencies.into_iter();
223    if self.options.context_options.namespace_object.is_false() {
224      return FakeMapValue::Bit(FakeNamespaceObjectMode::NAMESPACE);
225    }
226    let mut has_type = 0;
227    let mut fake_map = HashMap::default();
228    let module_graph = compilation.get_module_graph();
229    let sorted_modules = dependencies
230      .filter_map(|dep_id| {
231        module_graph
232          .module_identifier_by_dependency_id(dep_id)
233          .map(|m| (m, dep_id))
234      })
235      .filter_map(|(m, dep)| {
236        ChunkGraph::get_module_id(&compilation.module_ids_artifact, *m)
237          .map(|id| (id.to_string(), dep))
238      })
239      .sorted_unstable_by_key(|(module_id, _)| module_id.clone());
240    for (module_id, dep) in sorted_modules {
241      let exports_type = get_exports_type_with_strict(
242        compilation.get_module_graph(),
243        &compilation.module_graph_cache_artifact,
244        &compilation.exports_info_artifact,
245        dep,
246        matches!(
247          self.options.context_options.namespace_object,
248          ContextNameSpaceObject::Strict
249        ),
250      );
251      match exports_type {
252        ExportsType::Namespace => {
253          fake_map.insert(module_id, FakeNamespaceObjectMode::NAMESPACE);
254          has_type |= 1;
255        }
256        ExportsType::Dynamic => {
257          fake_map.insert(module_id, FakeNamespaceObjectMode::DYNAMIC);
258          has_type |= 2;
259        }
260        ExportsType::DefaultOnly => {
261          fake_map.insert(module_id, FakeNamespaceObjectMode::MODULE_ID);
262          has_type |= 4;
263        }
264        ExportsType::DefaultWithNamed => {
265          fake_map.insert(module_id, FakeNamespaceObjectMode::DEFAULT_WITH_NAMED);
266          has_type |= 8;
267        }
268      }
269    }
270
271    match has_type {
272      0 | 1 => FakeMapValue::Bit(FakeNamespaceObjectMode::NAMESPACE),
273      2 => FakeMapValue::Bit(FakeNamespaceObjectMode::DYNAMIC),
274      4 => FakeMapValue::Bit(FakeNamespaceObjectMode::MODULE_ID),
275      8 => FakeMapValue::Bit(FakeNamespaceObjectMode::DEFAULT_WITH_NAMED),
276      _ => FakeMapValue::Map(fake_map),
277    }
278  }
279
280  fn get_fake_map_init_statement(&self, fake_map: &FakeMapValue) -> String {
281    match fake_map {
282      FakeMapValue::Bit(_) => String::new(),
283      FakeMapValue::Map(map) => format!("var fakeMap = {}", json_stringify_pretty(map)),
284    }
285  }
286
287  fn get_module_deferred_async_deps_map<'a>(
288    &self,
289    dependencies: impl IntoIterator<Item = &'a DependencyId>,
290    compilation: &Compilation,
291  ) -> HashMap<String, Vec<ModuleId>> {
292    let module_graph = compilation.get_module_graph();
293    let mut map: HashMap<String, Vec<ModuleId>> = HashMap::default();
294    for dep_id in dependencies {
295      if let Some(module) = module_graph.get_module_by_dependency_id(dep_id)
296        && !module.build_meta().has_top_level_await
297      {
298        let id = ChunkGraph::get_module_id(&compilation.module_ids_artifact, module.identifier());
299        if let Some(id) = id {
300          let async_deps = get_outgoing_async_modules(compilation, module.as_ref());
301          map.insert(id.to_string(), async_deps.into_iter().collect());
302        }
303      }
304    }
305    map
306  }
307
308  fn get_module_deferred_async_deps_map_init_statement(
309    &self,
310    async_deps_map: Option<&HashMap<String, Vec<ModuleId>>>,
311  ) -> String {
312    match async_deps_map {
313      Some(map) => format!("var asyncDepsMap = {};", json_stringify_pretty(map)),
314      None => String::new(),
315    }
316  }
317
318  fn get_return_module_object_source(
319    &self,
320    fake_map: &FakeMapValue,
321    async_module: bool,
322    async_deps: Option<String>,
323    fake_map_data_expr: &str,
324    runtime_template: &mut ModuleCodeTemplate,
325  ) -> String {
326    let source = if let FakeMapValue::Bit(bit) = fake_map {
327      if *bit == FakeNamespaceObjectMode::NAMESPACE {
328        format!(
329          "{}(id)",
330          runtime_template.render_runtime_globals(&RuntimeGlobals::REQUIRE)
331        )
332      } else {
333        format!(
334          "{}(id, {}{})",
335          runtime_template.render_runtime_globals(&RuntimeGlobals::CREATE_FAKE_NAMESPACE_OBJECT),
336          bit,
337          if async_module { " | 16" } else { "" },
338        )
339      }
340    } else {
341      format!(
342        "{}(id, {}{})",
343        runtime_template.render_runtime_globals(&RuntimeGlobals::CREATE_FAKE_NAMESPACE_OBJECT),
344        fake_map_data_expr,
345        if async_module { " | 16" } else { "" },
346      )
347    };
348
349    if let Some(async_deps) = async_deps {
350      if !async_module {
351        panic!("Must be async when module is deferred");
352      }
353      let mode = if let FakeMapValue::Bit(bit) = fake_map {
354        Cow::Owned(bit.bits().to_string())
355      } else {
356        fake_map_data_expr.into()
357      };
358      let make_deferred =
359        runtime_template.render_runtime_globals(&RuntimeGlobals::MAKE_DEFERRED_NAMESPACE_OBJECT);
360      let async_transitive = runtime_template
361        .render_runtime_globals(&RuntimeGlobals::DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES);
362      let require = runtime_template.render_runtime_globals(&RuntimeGlobals::REQUIRE);
363      return format!(
364        "{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}"
365      );
366    }
367
368    source
369  }
370
371  fn get_user_request_map<'a>(
372    &self,
373    dependencies: impl IntoIterator<Item = &'a DependencyId>,
374    compilation: &Compilation,
375  ) -> FxIndexMap<String, Option<String>> {
376    let module_graph = compilation.get_module_graph();
377    let dependencies = dependencies.into_iter();
378    dependencies
379      .filter_map(|dep_id| {
380        let dependency = module_graph.dependency_by_id(dep_id);
381        let dep = if let Some(d) = dependency.as_module_dependency() {
382          Some(d.user_request().to_string())
383        } else {
384          dependency
385            .as_context_dependency()
386            .map(|d| d.request().to_string())
387        };
388        let module_id = module_graph
389          .module_identifier_by_dependency_id(dep_id)
390          .and_then(|module| ChunkGraph::get_module_id(&compilation.module_ids_artifact, *module))
391          .map(|s| s.to_string());
392        // module_id could be None in weak mode
393        dep.map(|dep| (dep, module_id))
394      })
395      .sorted_by(|(a, _), (b, _)| a.cmp(b))
396      .collect()
397  }
398
399  fn get_source_for_empty_async_context(
400    &self,
401    compilation: &Compilation,
402    runtime_template: &mut ModuleCodeTemplate,
403  ) -> String {
404    formatdoc! {r#"
405      function webpackEmptyAsyncContext(req) {{
406        // Here Promise.resolve().then() is used instead of new Promise() to prevent
407        // uncaught exception popping up in devtools
408        return Promise.resolve().then(function() {{
409          var e = new Error("Cannot find module '" + req + "'");
410          e.code = 'MODULE_NOT_FOUND';
411          throw e;
412        }});
413      }}
414      webpackEmptyAsyncContext.keys = {keys};
415      webpackEmptyAsyncContext.resolve = webpackEmptyAsyncContext;
416      webpackEmptyAsyncContext.id = {id};
417      {module}.exports = webpackEmptyAsyncContext;
418      "#,
419      module = runtime_template.render_module_argument(ModuleArgument::Module),
420      keys = runtime_template.returning_function("[]", ""),
421      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
422    }
423  }
424
425  fn get_source_for_empty_context(
426    &self,
427    compilation: &Compilation,
428    runtime_template: &mut ModuleCodeTemplate,
429  ) -> String {
430    formatdoc! {r#"
431      function webpackEmptyContext(req) {{
432        var e = new Error("Cannot find module '" + req + "'");
433        e.code = 'MODULE_NOT_FOUND';
434        throw e;
435      }}
436      webpackEmptyContext.keys = {keys};
437      webpackEmptyContext.resolve = webpackEmptyContext;
438      webpackEmptyContext.id = {id};
439      {module}.exports = webpackEmptyContext;
440      "#,
441      module = runtime_template.render_module_argument(ModuleArgument::Module),
442      keys = runtime_template.returning_function("[]", ""),
443      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
444    }
445  }
446
447  #[inline]
448  fn get_source_string(
449    &self,
450    compilation: &Compilation,
451    runtime_template: &mut ModuleCodeTemplate,
452  ) -> String {
453    match self.options.context_options.mode {
454      ContextMode::Lazy => {
455        if !self.get_blocks().is_empty() {
456          self.get_lazy_source(compilation, runtime_template)
457        } else {
458          self.get_source_for_empty_async_context(compilation, runtime_template)
459        }
460      }
461      ContextMode::Eager => {
462        if !self.get_dependencies().is_empty() {
463          self.get_eager_source(compilation, runtime_template)
464        } else {
465          self.get_source_for_empty_async_context(compilation, runtime_template)
466        }
467      }
468      ContextMode::LazyOnce => {
469        if let Some(block) = self.get_blocks().first() {
470          self.get_lazy_once_source(compilation, block, runtime_template)
471        } else {
472          self.get_source_for_empty_async_context(compilation, runtime_template)
473        }
474      }
475      ContextMode::AsyncWeak => {
476        if !self.get_dependencies().is_empty() {
477          self.get_async_weak_source(compilation, runtime_template)
478        } else {
479          self.get_source_for_empty_async_context(compilation, runtime_template)
480        }
481      }
482      ContextMode::Weak => {
483        if !self.get_dependencies().is_empty() {
484          self.get_sync_weak_source(compilation, runtime_template)
485        } else {
486          self.get_source_for_empty_context(compilation, runtime_template)
487        }
488      }
489      ContextMode::Sync => {
490        if !self.get_dependencies().is_empty() {
491          self.get_sync_source(compilation, runtime_template)
492        } else {
493          self.get_source_for_empty_context(compilation, runtime_template)
494        }
495      }
496    }
497  }
498
499  fn get_lazy_source(
500    &self,
501    compilation: &Compilation,
502    runtime_template: &mut ModuleCodeTemplate,
503  ) -> String {
504    let module_graph = compilation.get_module_graph();
505    let blocks = self
506      .get_blocks()
507      .iter()
508      .filter_map(|b| module_graph.block_by_id(b));
509    let block_and_first_dependency_list = blocks
510      .clone()
511      .filter_map(|b| b.get_dependencies().first().map(|d| (b, d)));
512    let first_dependencies = block_and_first_dependency_list.clone().map(|(_, d)| d);
513    let mut has_multiple_or_no_chunks = false;
514    let mut has_no_chunk = true;
515    let mut has_no_module_deferred = true;
516    let fake_map = self.get_fake_map(first_dependencies.clone(), compilation);
517    let has_fake_map = matches!(fake_map, FakeMapValue::Map(_));
518
519    let mut items = block_and_first_dependency_list
520      .filter_map(|(b, d)| {
521        let chunks: Vec<_> = compilation
522          .build_chunk_graph_artifact
523          .chunk_graph
524          .get_block_chunk_group(
525            &b.identifier(),
526            &compilation.build_chunk_graph_artifact.chunk_group_by_ukey,
527          )
528          .expect("should have block chunk group")
529          .chunks
530          .iter()
531          .map(|c| {
532            compilation
533              .build_chunk_graph_artifact
534              .chunk_by_ukey
535              .expect_get(c)
536              .id()
537              .expect("should have chunk id in code generation")
538          })
539          .collect();
540        if !chunks.is_empty() {
541          has_no_chunk = false;
542        }
543        if chunks.len() != 1 {
544          has_multiple_or_no_chunks = true;
545        }
546        let dependency = compilation.get_module_graph().dependency_by_id(d);
547        let user_request = dependency
548          .as_module_dependency()
549          .map(|d| d.user_request().to_string())
550          .or_else(|| {
551            dependency
552              .as_context_dependency()
553              .map(|d| d.request().to_string())
554          })?;
555        let module = module_graph.get_module_by_dependency_id(d)?;
556        let module_id =
557          ChunkGraph::get_module_id(&compilation.module_ids_artifact, module.identifier())?;
558        let async_deps = (self
559          .options
560          .context_options
561          .phase
562          .unwrap_or_default()
563          .is_defer()
564          && !module.build_meta().has_top_level_await)
565          .then(|| {
566            has_no_module_deferred = false;
567            get_outgoing_async_modules(compilation, module.as_ref())
568          });
569        Some((user_request, module_id.to_string(), chunks, async_deps))
570      })
571      .collect::<Vec<_>>();
572    let short_mode = has_no_chunk && has_no_module_deferred && !has_fake_map;
573    items.sort_unstable_by(|a, b| a.0.cmp(&b.0));
574    let map = items
575      .into_iter()
576      .map(|(user_request, module_id, chunks, async_deps)| {
577        let value = if short_mode {
578          serde_json::Value::String(module_id)
579        } else {
580          let mut array = vec![serde_json::json!(module_id)];
581          if let FakeMapValue::Map(fake_map) = &fake_map {
582            array.push(serde_json::json!(fake_map[&module_id].bits()));
583          }
584          if !has_no_chunk {
585            array.push(serde_json::json!(chunks));
586          }
587          if !has_no_module_deferred {
588            array.push(serde_json::json!(async_deps))
589          }
590          serde_json::json!(array)
591        };
592        (user_request, value)
593      })
594      .collect::<HashMap<_, _>>();
595
596    let chunks_position = if has_fake_map { 2 } else { 1 };
597    let async_deps_position = chunks_position + 1;
598    let request_prefix = if has_no_chunk {
599      "Promise.resolve()".to_string()
600    } else if has_multiple_or_no_chunks {
601      format!(
602        "Promise.all(ids[{chunks_position}].map({}))",
603        runtime_template.render_runtime_globals(&RuntimeGlobals::ENSURE_CHUNK)
604      )
605    } else {
606      let mut chunks_position_buffer = itoa::Buffer::new();
607      let chunks_position_str = chunks_position_buffer.format(chunks_position);
608      format!(
609        "{}(ids[{}][0])",
610        runtime_template.render_runtime_globals(&RuntimeGlobals::ENSURE_CHUNK),
611        chunks_position_str
612      )
613    };
614    let return_module_object = self.get_return_module_object_source(
615      &fake_map,
616      true,
617      if has_no_module_deferred {
618        None
619      } else {
620        Some(format!("ids[{async_deps_position}]"))
621      },
622      if short_mode { "invalid" } else { "ids[1]" },
623      runtime_template,
624    );
625
626    let has_own_property =
627      runtime_template.render_runtime_globals(&RuntimeGlobals::HAS_OWN_PROPERTY);
628    let async_context = if has_no_chunk {
629      let then_function = runtime_template.basic_function(
630        "",
631        &formatdoc! {
632          r#"if(!{has_own_property}(map, req)) {{
633            var e = new Error("Cannot find module '" + req + "'");
634            e.code = 'MODULE_NOT_FOUND';
635            throw e;
636          }}
637
638          {}
639          return {return_module_object};"#,
640          if short_mode {
641            "var id = map[req];"
642          } else {
643            "var ids = map[req], id = ids[0];"
644          }
645        },
646      );
647      formatdoc! {r#"
648        function __rspack_async_context(req) {{
649          return Promise.resolve().then({then_function});
650        }}
651      "#}
652    } else {
653      let then_function = runtime_template.returning_function(&return_module_object, "");
654      let module_not_found = runtime_template.basic_function(
655        "",
656        &formatdoc! {
657          r#"var e = new Error("Cannot find module '" + req + "'");
658            e.code = 'MODULE_NOT_FOUND';
659            throw e;"#
660        },
661      );
662      formatdoc! {r#"
663        function __rspack_async_context(req) {{
664          if(!{}(map, req)) {{
665            return Promise.resolve().then({module_not_found});
666          }}
667
668          var ids = map[req], id = ids[0];
669          return {request_prefix}.then({then_function});
670        }}
671        "#,
672        runtime_template.render_runtime_globals(&RuntimeGlobals::HAS_OWN_PROPERTY),
673      }
674    };
675
676    formatdoc! {r#"
677      var map = {map};
678      {async_context}
679      __rspack_async_context.keys = {keys};
680      __rspack_async_context.id = {id};
681      {module}.exports = __rspack_async_context;
682      "#,
683      module = runtime_template.render_module_argument(ModuleArgument::Module),
684      map = json_stringify_pretty(&map),
685      keys = runtime_template.returning_function("Object.keys(map)", ""),
686      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
687    }
688  }
689
690  fn get_lazy_once_source(
691    &self,
692    compilation: &Compilation,
693    block_id: &AsyncDependenciesBlockIdentifier,
694    runtime_template: &mut ModuleCodeTemplate,
695  ) -> String {
696    let mg = compilation.get_module_graph();
697    let block = mg.block_by_id_expect(block_id);
698    let dependencies = block.get_dependencies();
699    let promise = runtime_template.block_promise(Some(block_id), compilation, "lazy-once context");
700    let map = self.get_user_request_map(dependencies, compilation);
701    let fake_map = self.get_fake_map(dependencies, compilation);
702    let async_deps_map = self
703      .options
704      .context_options
705      .phase
706      .unwrap_or_default()
707      .is_defer()
708      .then(|| self.get_module_deferred_async_deps_map(dependencies, compilation));
709
710    let return_module_object_source = self.get_return_module_object_source(
711      &fake_map,
712      true,
713      async_deps_map
714        .is_some()
715        .then(|| "asyncDepsMap[id]".to_string()),
716      "fakeMap[id]",
717      runtime_template,
718    );
719    let then_function = runtime_template.returning_function(&return_module_object_source, "id");
720
721    let has_own_property =
722      runtime_template.render_runtime_globals(&RuntimeGlobals::HAS_OWN_PROPERTY);
723    let module_not_found = runtime_template.basic_function(
724      "",
725      &formatdoc! {
726        r#"if(!{has_own_property}(map, req)) {{
727          var e = new Error("Cannot find module '" + req + "'");
728          e.code = 'MODULE_NOT_FOUND';
729          throw e;
730        }}
731        return map[req];"#
732      },
733    );
734
735    formatdoc! {r#"
736      var map = {map};
737      {fake_map_init_statement}
738      {async_deps_map_init_statement}
739
740      function __rspack_async_context(req) {{
741        return __rspack_async_context_resolve(req).then({then_function});
742      }}
743      function __rspack_async_context_resolve(req) {{
744        return {promise}.then({module_not_found});
745      }}
746      __rspack_async_context.keys = {keys};
747      __rspack_async_context.resolve = __rspack_async_context_resolve;
748      __rspack_async_context.id = {id};
749      {module}.exports = __rspack_async_context;
750      "#,
751      module = runtime_template.render_module_argument(ModuleArgument::Module),
752      map = json_stringify_pretty(&map),
753      fake_map_init_statement = self.get_fake_map_init_statement(&fake_map),
754      async_deps_map_init_statement = self.get_module_deferred_async_deps_map_init_statement(async_deps_map.as_ref()),
755      keys = runtime_template.returning_function("Object.keys(map)", ""),
756      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
757    }
758  }
759
760  fn get_async_weak_source(
761    &self,
762    compilation: &Compilation,
763    runtime_template: &mut ModuleCodeTemplate,
764  ) -> String {
765    let dependencies = self.get_dependencies();
766    let map = self.get_user_request_map(dependencies, compilation);
767    let fake_map = self.get_fake_map(dependencies, compilation);
768    let async_deps_map = self
769      .options
770      .context_options
771      .phase
772      .unwrap_or_default()
773      .is_defer()
774      .then(|| self.get_module_deferred_async_deps_map(dependencies, compilation));
775
776    let return_module_object = self.get_return_module_object_source(
777      &fake_map,
778      true,
779      async_deps_map
780        .is_some()
781        .then(|| "asyncDepsMap[id]".to_string()),
782      "fakeMap[id]",
783      runtime_template,
784    );
785    let module_factories =
786      runtime_template.render_runtime_globals(&RuntimeGlobals::MODULE_FACTORIES);
787    let then_function = runtime_template.basic_function(
788      "id",
789      &formatdoc! {
790        r#"if(!{module_factories}[id]) {{
791          var e = new Error("Module '" + req + "' ('" + id + "') is not available (weak dependency)");
792          e.code = 'MODULE_NOT_FOUND';
793          throw e;
794        }}
795        return {return_module_object};"#
796      },
797    );
798    let has_own_property =
799      runtime_template.render_runtime_globals(&RuntimeGlobals::HAS_OWN_PROPERTY);
800    let module_not_found = runtime_template.basic_function(
801      "",
802      &formatdoc! {
803        r#"if(!{has_own_property}(map, req)) {{
804          var e = new Error("Cannot find module '" + req + "'");
805          e.code = 'MODULE_NOT_FOUND';
806          throw e;
807        }}
808        return map[req];"#
809      },
810    );
811
812    formatdoc! {r#"
813      var map = {map};
814      {fake_map_init_statement}
815      {async_deps_map_init_statement}
816
817      function __rspack_async_context(req) {{
818        return __rspack_async_context_resolve(req).then({then_function});
819      }}
820      function __rspack_async_context_resolve(req) {{
821        // Here Promise.resolve().then() is used instead of new Promise() to prevent
822        // uncaught exception popping up in devtools
823        return Promise.resolve().then({module_not_found});
824      }}
825      __rspack_async_context.keys = {keys};
826      __rspack_async_context.resolve = __rspack_async_context_resolve;
827      __rspack_async_context.id = {id};
828      {module}.exports = __rspack_async_context;
829      "#,
830      module = runtime_template.render_module_argument(ModuleArgument::Module),
831      map = json_stringify_pretty(&map),
832      fake_map_init_statement = self.get_fake_map_init_statement(&fake_map),
833      async_deps_map_init_statement = self.get_module_deferred_async_deps_map_init_statement(async_deps_map.as_ref()),
834      keys = runtime_template.returning_function("Object.keys(map)", ""),
835      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
836    }
837  }
838
839  fn get_sync_weak_source(
840    &self,
841    compilation: &Compilation,
842    runtime_template: &mut ModuleCodeTemplate,
843  ) -> String {
844    let dependencies = self.get_dependencies();
845    let map = self.get_user_request_map(dependencies, compilation);
846    let fake_map = self.get_fake_map(dependencies, compilation);
847    let return_module_object =
848      self.get_return_module_object_source(&fake_map, true, None, "fakeMap[id]", runtime_template);
849    formatdoc! {r#"
850      var map = {map};
851      {fake_map_init_statement}
852
853      function __rspack_context(req) {{
854        var id = __rspack_context_resolve(req);
855        if(!{module_factories}[id]) {{
856          var e = new Error("Module '" + req + "' ('" + id + "') is not available (weak dependency)");
857          e.code = 'MODULE_NOT_FOUND';
858          throw e;
859        }}
860        return {return_module_object};
861      }}
862      function __rspack_context_resolve(req) {{
863        if(!{has_own_property}(map, req)) {{
864          var e = new Error("Cannot find module '" + req + "'");
865          e.code = 'MODULE_NOT_FOUND';
866          throw e;
867        }}
868        return map[req];
869      }}
870      __rspack_context.keys = {keys};
871      __rspack_context.resolve = __rspack_context_resolve;
872      __rspack_context.id = {id};
873      {module}.exports = __rspack_context;
874      "#,
875      module = runtime_template.render_module_argument(ModuleArgument::Module),
876      map = json_stringify_pretty(&map),
877      fake_map_init_statement = self.get_fake_map_init_statement(&fake_map),
878      module_factories = runtime_template.render_runtime_globals(&RuntimeGlobals::MODULE_FACTORIES),
879      has_own_property = runtime_template.render_runtime_globals(&RuntimeGlobals::HAS_OWN_PROPERTY),
880      keys = runtime_template.returning_function("Object.keys(map)", ""),
881      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
882    }
883  }
884
885  fn get_eager_source(
886    &self,
887    compilation: &Compilation,
888    runtime_template: &mut ModuleCodeTemplate,
889  ) -> String {
890    let dependencies = self.get_dependencies();
891    let map = self.get_user_request_map(dependencies, compilation);
892    let fake_map = self.get_fake_map(dependencies, compilation);
893    let async_deps_map = self
894      .options
895      .context_options
896      .phase
897      .unwrap_or_default()
898      .is_defer()
899      .then(|| self.get_module_deferred_async_deps_map(dependencies, compilation));
900    let return_module_object_source = self.get_return_module_object_source(
901      &fake_map,
902      true,
903      async_deps_map
904        .is_some()
905        .then(|| "asyncDepsMap[id]".to_string()),
906      "fakeMap[id]",
907      runtime_template,
908    );
909    let then_function = runtime_template.returning_function(&return_module_object_source, "id");
910    let has_own_property =
911      runtime_template.render_runtime_globals(&RuntimeGlobals::HAS_OWN_PROPERTY);
912    let module_not_found = runtime_template.basic_function(
913      "",
914      &formatdoc! {
915        r#"if(!{has_own_property}(map, req)) {{
916          var e = new Error("Cannot find module '" + req + "'");
917          e.code = 'MODULE_NOT_FOUND';
918          throw e;
919        }}
920        return map[req];"#
921      },
922    );
923
924    formatdoc! {r#"
925      var map = {map};
926      {fake_map_init_statement}
927      {async_deps_map_init_statement}
928
929      function __rspack_async_context(req) {{
930        return __rspack_async_context_resolve(req).then({then_function});
931      }}
932      function __rspack_async_context_resolve(req) {{
933        // Here Promise.resolve().then() is used instead of new Promise() to prevent
934        // uncaught exception popping up in devtools
935        return Promise.resolve().then({module_not_found});
936      }}
937      __rspack_async_context.keys = {keys};
938      __rspack_async_context.resolve = __rspack_async_context_resolve;
939      __rspack_async_context.id = {id};
940      {module}.exports = __rspack_async_context;
941      "#,
942      module = runtime_template.render_module_argument(ModuleArgument::Module),
943      map = json_stringify_pretty(&map),
944      fake_map_init_statement = self.get_fake_map_init_statement(&fake_map),
945      async_deps_map_init_statement = self.get_module_deferred_async_deps_map_init_statement(async_deps_map.as_ref()),
946      keys = runtime_template.returning_function("Object.keys(map)", ""),
947      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
948    }
949  }
950
951  fn get_sync_source(
952    &self,
953    compilation: &Compilation,
954    runtime_template: &mut ModuleCodeTemplate,
955  ) -> String {
956    let dependencies = self.get_dependencies();
957    let map = self.get_user_request_map(dependencies, compilation);
958    let fake_map = self.get_fake_map(dependencies, compilation);
959    let return_module_object =
960      self.get_return_module_object_source(&fake_map, false, None, "fakeMap[id]", runtime_template);
961    formatdoc! {r#"
962      var map = {map};
963      {fake_map_init_statement}
964
965      function __rspack_context(req) {{
966        var id = __rspack_context_resolve(req);
967        return {return_module_object};
968      }}
969      function __rspack_context_resolve(req) {{
970        if(!{has_own_property}(map, req)) {{
971          var e = new Error("Cannot find module '" + req + "'");
972          e.code = 'MODULE_NOT_FOUND';
973          throw e;
974        }}
975        return map[req];
976      }}
977      __rspack_context.keys = {keys};
978      __rspack_context.resolve = __rspack_context_resolve;
979      {module}.exports = __rspack_context;
980      __rspack_context.id = {id};
981      "#,
982      module = runtime_template.render_module_argument(ModuleArgument::Module),
983      map = json_stringify_pretty(&map),
984      fake_map_init_statement = self.get_fake_map_init_statement(&fake_map),
985      has_own_property = runtime_template.render_runtime_globals(&RuntimeGlobals::HAS_OWN_PROPERTY),
986      keys = runtime_template.returning_function("Object.keys(map)", ""),
987      id = json_stringify(self.get_module_id(&compilation.module_ids_artifact))
988    }
989  }
990
991  fn get_source(&self, source_string: String, compilation: &Compilation) -> BoxSource {
992    let source_map_kind = self.get_source_map_kind();
993    if source_map_kind.enabled() {
994      OriginalSource::new(
995        source_string,
996        format!(
997          "webpack://{}",
998          make_paths_relative(&compilation.options.context, self.identifier.as_str(),)
999        ),
1000      )
1001      .boxed()
1002    } else {
1003      RawStringSource::from(source_string).boxed()
1004    }
1005  }
1006}
1007
1008impl DependenciesBlock for ContextModule {
1009  fn add_block_id(&mut self, block: AsyncDependenciesBlockIdentifier) {
1010    self.blocks.push(block)
1011  }
1012
1013  fn get_blocks(&self) -> &[AsyncDependenciesBlockIdentifier] {
1014    &self.blocks
1015  }
1016
1017  fn add_dependency_id(&mut self, dependency: DependencyId) {
1018    self.dependencies.push(dependency)
1019  }
1020
1021  fn remove_dependency_id(&mut self, dependency: DependencyId) {
1022    self.dependencies.retain(|d| d != &dependency)
1023  }
1024
1025  fn get_dependencies(&self) -> &[DependencyId] {
1026    &self.dependencies
1027  }
1028}
1029
1030#[cacheable_dyn]
1031#[async_trait::async_trait]
1032impl Module for ContextModule {
1033  impl_module_meta_info!();
1034
1035  fn module_type(&self) -> &ModuleType {
1036    &ModuleType::JsAuto
1037  }
1038
1039  fn source_types(&self, _module_graph: &ModuleGraph) -> &[SourceType] {
1040    &[SourceType::JavaScript]
1041  }
1042
1043  fn source(&self) -> Option<&rspack_sources::BoxSource> {
1044    None
1045  }
1046
1047  fn readable_identifier(&self, context: &crate::Context) -> std::borrow::Cow<'_, str> {
1048    let identifier = contextify(
1049      context,
1050      if self.options.resource.as_str().is_empty() {
1051        "false"
1052      } else {
1053        self.options.resource.as_str()
1054      },
1055    );
1056    create_identifier(&self.options, Some(identifier.as_str()))
1057      .to_string()
1058      .into()
1059  }
1060
1061  fn size(
1062    &self,
1063    _source_type: Option<&crate::SourceType>,
1064    _compilation: Option<&Compilation>,
1065  ) -> f64 {
1066    160.0
1067  }
1068
1069  fn lib_ident(&self, options: LibIdentOptions) -> Option<Cow<'_, str>> {
1070    let mut id = String::new();
1071    if let Some(layer) = &self.options.layer {
1072      id += "(";
1073      id += layer;
1074      id += ")/";
1075    }
1076    id += &contextify(
1077      options.context,
1078      if self.options.resource.as_str().is_empty() {
1079        "false"
1080      } else {
1081        self.options.resource.as_str()
1082      },
1083    );
1084    id += " ";
1085    id += self.options.context_options.mode.as_str();
1086    if self.options.context_options.recursive {
1087      id += " recursive";
1088    }
1089    if !self.options.addon.is_empty() {
1090      id += " ";
1091      id += &self.options.addon;
1092    }
1093    if let Some(regexp) = &self.options.context_options.reg_exp {
1094      id += " ";
1095      id += &regexp.to_pretty_string(true);
1096    }
1097    if let Some(include) = &self.options.context_options.include {
1098      id += " include: ";
1099      id += &include.to_pretty_string(true);
1100    }
1101    if let Some(exclude) = &self.options.context_options.exclude {
1102      id += " exclude: ";
1103      id += &exclude.to_pretty_string(true);
1104    }
1105    if let Some(specifiers) = &self.options.context_options.referenced_specifiers {
1106      id += " referencedExports: ";
1107      id += &specifiers
1108        .iter()
1109        .map(|specifier| {
1110          let s = specifier.names.iter().join(".");
1111          if specifier.namespace_object_as_context && specifier.is_call {
1112            format!("*.{s}()")
1113          } else {
1114            s
1115          }
1116        })
1117        .join(", ");
1118    }
1119    Some(Cow::Owned(id))
1120  }
1121
1122  async fn build(
1123    mut self: Box<Self>,
1124    _build_context: BuildContext,
1125    _: Option<&Compilation>,
1126  ) -> Result<BuildResult> {
1127    let resolve_dependencies = &self.resolve_dependencies;
1128    let context_element_dependencies = resolve_dependencies(self.options.clone()).await?;
1129
1130    let mut dependencies: Vec<BoxDependency> = vec![];
1131    let mut blocks = vec![];
1132    if matches!(self.options.context_options.mode, ContextMode::LazyOnce)
1133      && !context_element_dependencies.is_empty()
1134    {
1135      let loc = DependencyLocation::Real(RealDependencyLocation::new(
1136        (
1137          self.options.context_options.start,
1138          self.options.context_options.end,
1139        )
1140          .into(),
1141        None,
1142      ));
1143      let mut block = AsyncDependenciesBlock::new(
1144        (*self.identifier).into(),
1145        Some(loc),
1146        None,
1147        context_element_dependencies
1148          .into_iter()
1149          .map(|dep| Box::new(dep) as Box<dyn Dependency>)
1150          .collect(),
1151        None,
1152      );
1153      if let Some(group_options) = &self.options.context_options.group_options {
1154        block.set_group_options(group_options.clone());
1155      }
1156      blocks.push(Box::new(block));
1157    } else if matches!(self.options.context_options.mode, ContextMode::Lazy) {
1158      let mut index = 0;
1159      for context_element_dependency in context_element_dependencies {
1160        let group_options = self
1161          .options
1162          .context_options
1163          .group_options
1164          .as_ref()
1165          .and_then(|g| g.normal_options());
1166        let name = group_options
1167          .and_then(|group_options| group_options.name.as_ref())
1168          .map(|name| {
1169            let name = if !(name.contains(CHUNK_NAME_INDEX_PLACEHOLDER)
1170              || name.contains(CHUNK_NAME_REQUEST_PLACEHOLDER))
1171            {
1172              Cow::Owned(format!("{name}{CHUNK_NAME_INDEX_PLACEHOLDER}"))
1173            } else {
1174              Cow::Borrowed(name)
1175            };
1176
1177            let name = name.cow_replace(CHUNK_NAME_INDEX_PLACEHOLDER, &index.to_string());
1178            let name = name.cow_replace(
1179              CHUNK_NAME_REQUEST_PLACEHOLDER,
1180              &to_path(&context_element_dependency.user_request),
1181            );
1182
1183            index += 1;
1184            name.into_owned()
1185          });
1186        let preload_order = group_options.and_then(|o| o.preload_order);
1187        let prefetch_order = group_options.and_then(|o| o.prefetch_order);
1188        let fetch_priority = group_options.and_then(|o| o.fetch_priority);
1189        let mut block = AsyncDependenciesBlock::new(
1190          (*self.identifier).into(),
1191          None,
1192          Some(&context_element_dependency.user_request.clone()),
1193          vec![Box::new(context_element_dependency)],
1194          Some(self.options.context_options.request.clone()),
1195        );
1196        block.set_group_options(GroupOptions::ChunkGroup(ChunkGroupOptions::new(
1197          name,
1198          preload_order,
1199          prefetch_order,
1200          fetch_priority,
1201        )));
1202        blocks.push(Box::new(block));
1203      }
1204    } else {
1205      dependencies = context_element_dependencies
1206        .into_iter()
1207        .map(|d| Box::new(d) as BoxDependency)
1208        .collect();
1209    }
1210
1211    if !self.options.resource.as_str().is_empty() {
1212      let mut context_dependencies: ArcPathSet = Default::default();
1213      context_dependencies.insert(self.options.resource.as_std_path().into());
1214      self.build_info.context_dependencies = context_dependencies;
1215    }
1216
1217    Ok(BuildResult {
1218      module: BoxModule::new(self),
1219      dependencies,
1220      blocks,
1221      optimization_bailouts: vec![],
1222    })
1223  }
1224
1225  // #[tracing::instrument("ContextModule::code_generation", skip_all, fields(identifier = ?self.identifier()))]
1226  async fn code_generation(
1227    &self,
1228    code_generation_context: &mut ModuleCodeGenerationContext,
1229  ) -> Result<CodeGenerationResult> {
1230    let ModuleCodeGenerationContext {
1231      compilation,
1232      runtime_template,
1233      ..
1234    } = code_generation_context;
1235    let mut code_generation_result = CodeGenerationResult::default();
1236    let source = self.get_source(
1237      self.get_source_string(compilation, runtime_template),
1238      compilation,
1239    );
1240    code_generation_result.add(SourceType::JavaScript, source);
1241    let mut all_deps = self.get_dependencies().to_vec();
1242    let module_graph = compilation.get_module_graph();
1243    for block in self.get_blocks() {
1244      let block = module_graph
1245        .block_by_id(block)
1246        .expect("should have block in ContextModule code_generation");
1247      all_deps.extend(block.get_dependencies());
1248    }
1249
1250    Ok(code_generation_result)
1251  }
1252
1253  async fn get_runtime_hash(
1254    &self,
1255    compilation: &Compilation,
1256    runtime: Option<&RuntimeSpec>,
1257  ) -> Result<RspackHashDigest> {
1258    let mut hasher = RspackHash::from(&compilation.options.output);
1259    module_update_hash(self, &mut hasher, compilation, runtime);
1260    Ok(hasher.digest(&compilation.options.output.hash_digest))
1261  }
1262}
1263
1264impl_empty_diagnosable_trait!(ContextModule);
1265
1266impl Identifiable for ContextModule {
1267  fn identifier(&self) -> Identifier {
1268    self.identifier
1269  }
1270}
1271
1272fn create_identifier(options: &ContextModuleOptions, resource: Option<&str>) -> Identifier {
1273  let mut id = resource
1274    .unwrap_or(if options.resource.as_str().is_empty() {
1275      "false"
1276    } else {
1277      options.resource.as_str()
1278    })
1279    .to_owned();
1280  if !options.resource_query.is_empty() {
1281    id += "|";
1282    id += &options.resource_query;
1283  }
1284  if !options.resource_fragment.is_empty() {
1285    id += "|";
1286    id += &options.resource_fragment;
1287  }
1288  id += "|";
1289  id += options.context_options.mode.as_str();
1290  if !options.context_options.recursive {
1291    id += "|nonrecursive";
1292  }
1293  if !options.addon.is_empty() {
1294    id += "|";
1295    id += &options.addon;
1296  }
1297  if let Some(regexp) = &options.context_options.reg_exp {
1298    id += "|";
1299    id += &regexp.to_pretty_string(false);
1300  }
1301  if let Some(include) = &options.context_options.include {
1302    id += "|include: ";
1303    id += &include.to_source_string();
1304  }
1305  if let Some(exclude) = &options.context_options.exclude {
1306    id += "|exclude: ";
1307    id += &exclude.to_source_string();
1308  }
1309  if let Some(specifiers) = &options.context_options.referenced_specifiers {
1310    id += "|referencedExports: ";
1311    id += &specifiers
1312      .iter()
1313      .map(|specifier| {
1314        if specifier.namespace_object_as_context && specifier.is_call {
1315          format!("*.{}()", specifier.names.iter().join("."))
1316        } else {
1317          specifier.names.iter().join(".")
1318        }
1319      })
1320      .join(", ");
1321  }
1322
1323  if let Some(GroupOptions::ChunkGroup(group)) = &options.context_options.group_options {
1324    if let Some(chunk_name) = &group.name {
1325      id += "|chunkName: ";
1326      id += chunk_name;
1327    }
1328    id += "|groupOptions: {";
1329    if let Some(o) = group.prefetch_order {
1330      write!(id, "prefetchOrder: {o},").expect("infallible write to String");
1331    }
1332    if let Some(o) = group.preload_order {
1333      write!(id, "preloadOrder: {o},").expect("infallible write to String");
1334    }
1335    if let Some(o) = group.fetch_priority {
1336      write!(id, "fetchPriority: {o},").expect("infallible write to String");
1337    }
1338    id += "}";
1339  }
1340  id += match options.context_options.namespace_object {
1341    ContextNameSpaceObject::Strict => "|strict namespace object",
1342    ContextNameSpaceObject::Bool(true) => "|namespace object",
1343    _ => "",
1344  };
1345  if let Some(attributes) = &options.context_options.attributes {
1346    id += "|importAttributes: ";
1347    id += &serde_json::to_string(attributes).expect("json stringify failed");
1348  }
1349  if let Some(phase) = &options.context_options.phase {
1350    id += "|importPhase: ";
1351    id += phase.as_str();
1352  }
1353  if let Some(layer) = &options.layer {
1354    id += "|layer: ";
1355    id += layer;
1356  }
1357  id.into()
1358}