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 _ => 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 _ => 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 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 += ®exp.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 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 += ®exp.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}