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