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