1use std::{
2 any::Any,
3 borrow::Cow,
4 fmt::{Debug, Display, Formatter},
5 sync::Arc,
6};
7
8use async_trait::async_trait;
9use json::JsonValue;
10use rspack_cacheable::{
11 cacheable, cacheable_dyn,
12 with::{AsInner, AsInnerConverter, AsMap, AsOption, AsPreset, AsVec},
13};
14use rspack_collections::{Identifiable, Identifier, IdentifierMap, IdentifierSet};
15use rspack_error::{Diagnosable, Result};
16use rspack_fs::ReadableFileSystem;
17use rspack_hash::{RspackHash, RspackHashDigest, RspackHasher, write_u64_hex};
18use rspack_paths::ArcPathSet;
19use rspack_sources::BoxSource;
20use rspack_util::{
21 atom::Atom,
22 ext::AsAny,
23 fx_hash::{FxIndexMap, FxIndexSet},
24 source_map::ModuleSourceMapConfig,
25};
26use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
27use serde::Serialize;
28use smol_str::SmolStr;
29use swc_core::atoms::Wtf8Atom;
30
31use crate::{
32 AsyncDependenciesBlock, BindingCell, BoxDependency, BoxDependencyTemplate, BoxModuleDependency,
33 ChunkGraph, ChunkUkey, CodeGenerationResult, CollectedTypeScriptInfo, Compilation,
34 CompilationAsset, CompilationId, CompilerId, CompilerOptions, ConcatenationScope,
35 ConcatenationScopeInfoMode, ConnectionState, Context, ContextModule, CssExportType,
36 DependenciesBlock, DependencyId, ExportProvided, ExportsInfoArtifact, ExternalModule, Filename,
37 GetTargetResult, ImportPhase, ModuleCodeTemplate, ModuleGraph, ModuleGraphCacheArtifact,
38 ModuleLayer, ModuleType, NormalModule, OptimizationBailoutItem, RawModule, Resolve,
39 ResolverFactory, RuntimeSpec, SelfModule, SharedPluginDriver, SideEffectsStateArtifact,
40 SourceType, concatenated_module::ConcatenatedModule,
41 dependencies_block::dependencies_block_update_hash, get_target,
42 utils::PendingConcatenationScopeInfo, value_cache_versions::ValueCacheVersions,
43};
44
45pub struct BuildContext {
46 pub compiler_id: CompilerId,
47 pub compilation_id: CompilationId,
48 pub compiler_options: Arc<CompilerOptions>,
49 pub resolver_factory: Arc<ResolverFactory>,
50 pub runtime_template: ModuleCodeTemplate,
51 pub plugin_driver: SharedPluginDriver,
52 pub fs: Arc<dyn ReadableFileSystem>,
53}
54
55#[cacheable]
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
57pub enum RscModuleType {
58 ServerEntry,
67 Server,
68 Client,
69}
70
71#[cacheable]
72#[derive(Debug, Clone)]
73pub struct RscMeta {
74 pub module_type: RscModuleType,
75
76 #[cacheable(with=AsVec<AsPreset>)]
77 pub server_refs: Vec<Wtf8Atom>,
78
79 #[cacheable(with=AsVec<AsPreset>)]
80 pub client_refs: Vec<Wtf8Atom>,
81
82 pub import_meta_rsc: bool,
88
89 pub is_cjs: bool,
90
91 #[cacheable(with=AsMap<AsPreset, AsPreset>)]
92 pub action_ids: FxIndexMap<Atom, Atom>,
93}
94
95#[cacheable]
96#[derive(Debug, Clone)]
97pub enum CanonicalizedDataUrlOption {
98 Source,
99 Bytes,
100 Asset(bool),
101}
102
103impl CanonicalizedDataUrlOption {
104 pub fn is_source(&self) -> bool {
105 matches!(self, Self::Source)
106 }
107
108 pub fn is_bytes(&self) -> bool {
109 matches!(self, Self::Bytes)
110 }
111
112 pub fn is_inline(&self) -> bool {
113 matches!(self, Self::Asset(true))
114 }
115
116 pub fn is_resource(&self) -> bool {
117 matches!(self, Self::Asset(false))
118 }
119}
120
121#[cacheable]
122#[derive(Debug, Clone, PartialEq, Eq, Hash)]
123pub struct CssExport {
124 #[cacheable(with=AsPreset)]
125 pub ident: SmolStr,
126 #[cacheable(with=AsOption<AsPreset>)]
127 pub from: Option<SmolStr>,
128 pub id: Option<DependencyId>,
129 #[cacheable(with=AsPreset)]
130 pub orig_name: SmolStr,
131}
132
133pub type CssExports = FxIndexMap<SmolStr, FxIndexSet<CssExport>>;
134pub type CssLocalNames = HashMap<SmolStr, SmolStr>;
135
136#[cacheable]
137#[derive(Debug, Clone, PartialEq, Eq, Hash)]
138pub enum CssLayer {
139 Anonymous,
140 Named(#[cacheable(with=AsPreset)] SmolStr),
141}
142
143#[cacheable]
144#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
145pub struct CssModuleRenderCondition {
146 #[cacheable(with=AsOption<AsPreset>)]
147 pub media: Option<SmolStr>,
148 #[cacheable(with=AsOption<AsPreset>)]
149 pub supports: Option<SmolStr>,
150 pub layer: Option<CssLayer>,
151}
152
153impl CssModuleRenderCondition {
154 pub fn new(media: Option<SmolStr>, supports: Option<SmolStr>, layer: Option<CssLayer>) -> Self {
155 Self {
156 media,
157 supports,
158 layer,
159 }
160 }
161
162 pub fn is_empty(&self) -> bool {
163 self.media.is_none() && self.supports.is_none() && self.layer.is_none()
164 }
165}
166
167pub fn iter_css_module_render_conditions<'a>(
168 inherited_render_conditions: &'a [CssModuleRenderCondition],
169 render_condition: &'a CssModuleRenderCondition,
170) -> impl Iterator<Item = &'a CssModuleRenderCondition> {
171 inherited_render_conditions
172 .iter()
173 .chain(std::iter::once(render_condition))
174 .filter(|condition| !condition.is_empty())
175}
176
177pub fn css_module_render_conditions_identifier<'a>(
178 conditions: impl IntoIterator<Item = &'a CssModuleRenderCondition>,
179) -> Option<String> {
180 let mut key = String::new();
181 let mut count = 0;
182 for condition in conditions
183 .into_iter()
184 .filter(|condition| !condition.is_empty())
185 {
186 count += 1;
187 let layer = match &condition.layer {
188 Some(CssLayer::Anonymous) => "<anonymous>",
189 Some(CssLayer::Named(layer)) => layer.as_str(),
190 None => "",
191 };
192 push_css_module_identifier_part(&mut key, layer);
193 push_css_module_identifier_part(&mut key, condition.supports.as_deref().unwrap_or_default());
194 push_css_module_identifier_part(&mut key, condition.media.as_deref().unwrap_or_default());
195 }
196
197 if count == 0 {
198 None
199 } else {
200 Some(format!("conditions={count}{key}"))
201 }
202}
203
204pub fn push_css_module_identifier_part(identifier: &mut String, value: &str) {
205 identifier.push('|');
206 identifier.push_str(&value.len().to_string());
207 identifier.push(':');
208 identifier.push_str(value);
209}
210
211#[cacheable]
212#[derive(Debug, Clone, Default)]
213pub struct CssBuildInfo {
214 pub export_type: Option<CssExportType>,
215 pub has_charset: bool,
216 pub css_import_dependency: bool,
217 #[cacheable(with=AsMap<AsPreset, AsVec>)]
218 pub exports: CssExports,
219 #[cacheable(with=AsMap<AsPreset, AsPreset>)]
220 pub local_names: CssLocalNames,
221 pub inherited_render_conditions: Vec<CssModuleRenderCondition>,
226 pub render_condition: CssModuleRenderCondition,
227}
228
229impl CssBuildInfo {
230 pub fn exports(&self) -> Option<&CssExports> {
231 (!self.exports.is_empty()).then_some(&self.exports)
232 }
233
234 pub fn local_names(&self) -> Option<&CssLocalNames> {
235 (!self.local_names.is_empty()).then_some(&self.local_names)
236 }
237
238 pub fn render_conditions(&self) -> impl Iterator<Item = &CssModuleRenderCondition> {
239 iter_css_module_render_conditions(&self.inherited_render_conditions, &self.render_condition)
240 }
241
242 pub fn has_render_conditions(&self) -> bool {
243 self.render_conditions().next().is_some()
244 }
245}
246
247#[cacheable]
248#[derive(Debug, Clone)]
249pub struct IsolatedDts {
250 pub resource_path: String,
251 pub code: String,
252 pub references: Vec<String>,
253}
254
255#[cacheable]
256#[derive(Debug, Clone)]
257pub struct AssetBuildInfo {
258 pub data_url: CanonicalizedDataUrlOption,
259 pub filename: Option<Filename>,
260}
261
262#[cacheable]
263#[derive(Debug, Clone)]
264pub struct BuildInfo {
265 pub cacheable: bool,
267 pub hash: Option<RspackHashDigest>,
268 pub strict: bool,
269 pub module_argument: ModuleArgument,
270 pub exports_argument: ExportsArgument,
271 pub file_dependencies: ArcPathSet,
272 pub context_dependencies: ArcPathSet,
273 pub missing_dependencies: ArcPathSet,
274 pub build_dependencies: ArcPathSet,
275 pub value_dependencies: HashMap<String, String>,
276 #[cacheable(with=AsVec<AsPreset>)]
277 pub esm_named_exports: HashSet<Atom>,
278 pub all_star_exports: Vec<DependencyId>,
279 pub need_create_require: bool,
280 #[cacheable(with=AsOption<AsPreset>)]
281 pub json_data: Option<JsonValue>,
282 pub asset: Option<Box<AssetBuildInfo>>,
283 pub css: Option<Box<CssBuildInfo>>,
284 #[cacheable(with=AsOption<AsVec<AsPreset>>)]
285 pub side_effects_free: Option<HashSet<Atom>>,
286 #[cacheable(with=AsOption<AsVec<AsPreset>>)]
287 pub top_level_declarations: Option<HashSet<Atom>>,
288 pub pending_concatenation_scope_info: Option<Box<PendingConcatenationScopeInfo>>,
289 pub module_concatenation_bailout: Option<String>,
290 pub assets: BindingCell<HashMap<String, CompilationAsset>>,
291 pub module: bool,
292 pub inline_exports: bool,
293 pub collected_typescript_info: Option<CollectedTypeScriptInfo>,
294 pub rsc: Option<RscMeta>,
295 pub import_phase: ImportPhase,
296 pub isolated_dts: Option<Box<IsolatedDts>>,
297 #[cacheable(with=AsPreset)]
300 pub extras: serde_json::Map<String, serde_json::Value>,
301 #[cacheable(with=AsVec)]
302 pub deferred_pure_checks: HashSet<DeferredPureCheck>,
303}
304
305impl Default for BuildInfo {
306 fn default() -> Self {
307 Self {
308 cacheable: true,
309 hash: None,
310 strict: false,
311 module_argument: Default::default(),
312 exports_argument: Default::default(),
313 file_dependencies: ArcPathSet::default(),
314 context_dependencies: ArcPathSet::default(),
315 missing_dependencies: ArcPathSet::default(),
316 build_dependencies: ArcPathSet::default(),
317 value_dependencies: HashMap::default(),
318 esm_named_exports: HashSet::default(),
319 all_star_exports: Vec::default(),
320 need_create_require: false,
321 json_data: None,
322 asset: None,
323 css: None,
324 side_effects_free: None,
325 top_level_declarations: None,
326 pending_concatenation_scope_info: None,
327 module_concatenation_bailout: None,
328 assets: Default::default(),
329 module: false,
330 inline_exports: false,
331 collected_typescript_info: None,
332 rsc: None,
333 import_phase: ImportPhase::Evaluation,
334 isolated_dts: None,
335 extras: Default::default(),
336 deferred_pure_checks: HashSet::default(),
337 }
338 }
339}
340
341#[cacheable]
342#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize)]
343#[serde(rename_all = "camelCase")]
344pub enum BuildMetaExportsType {
345 #[default]
346 Unset,
347 Default,
348 Namespace,
349 Flagged,
350 Dynamic,
351}
352
353impl From<&str> for BuildMetaExportsType {
354 fn from(value: &str) -> Self {
355 match value {
356 "unset" => BuildMetaExportsType::Unset,
357 "default" => BuildMetaExportsType::Default,
358 "namespace" => BuildMetaExportsType::Namespace,
359 "flagged" => BuildMetaExportsType::Flagged,
360 "dynamic" => BuildMetaExportsType::Dynamic,
361 _ => unreachable!(),
362 }
363 }
364}
365
366impl Display for BuildMetaExportsType {
367 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
368 f.write_str(self.as_str())
369 }
370}
371
372impl BuildMetaExportsType {
373 fn as_str(&self) -> &'static str {
374 match self {
375 BuildMetaExportsType::Unset => "unset",
376 BuildMetaExportsType::Default => "default",
377 BuildMetaExportsType::Namespace => "namespace",
378 BuildMetaExportsType::Flagged => "flagged",
379 BuildMetaExportsType::Dynamic => "dynamic",
380 }
381 }
382
383 pub fn description(&self) -> &'static str {
384 match self {
385 BuildMetaExportsType::Unset => "unknown exports (runtime-defined)",
386 BuildMetaExportsType::Default => "default exports",
387 BuildMetaExportsType::Namespace => "namespace exports",
388 BuildMetaExportsType::Flagged => "flagged exports",
389 BuildMetaExportsType::Dynamic => "dynamic exports",
390 }
391 }
392}
393
394#[derive(Debug, Clone, Copy, Hash)]
395pub enum ExportsType {
396 DefaultOnly,
397 Namespace,
398 DefaultWithNamed,
399 Dynamic,
400}
401
402impl Display for ExportsType {
403 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
404 f.write_str(self.as_str())
405 }
406}
407
408impl ExportsType {
409 fn as_str(&self) -> &'static str {
410 match self {
411 ExportsType::DefaultOnly => "default-only",
412 ExportsType::Namespace => "namespace",
413 ExportsType::DefaultWithNamed => "default-with-named",
414 ExportsType::Dynamic => "dynamic",
415 }
416 }
417}
418
419#[cacheable]
420#[derive(Debug, Default, Clone, Copy, Serialize)]
421#[serde(rename_all = "camelCase")]
422pub enum BuildMetaDefaultObject {
423 #[default]
424 False,
425 Redirect,
426 #[serde(rename = "redirect-warn")]
429 RedirectWarn,
430}
431
432impl Display for BuildMetaDefaultObject {
433 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
434 f.write_str(self.as_str())
435 }
436}
437
438impl BuildMetaDefaultObject {
439 fn as_str(&self) -> &'static str {
440 match self {
441 BuildMetaDefaultObject::False => "false",
442 BuildMetaDefaultObject::Redirect => "redirect",
443 BuildMetaDefaultObject::RedirectWarn => "redirect-warn",
444 }
445 }
446}
447
448#[cacheable]
449#[derive(Debug, Clone, PartialEq, Eq, Hash)]
450pub struct DeferredPureCheck {
451 #[cacheable(with=AsPreset)]
452 pub atom: Atom,
453 pub dep_id: DependencyId,
454 pub start: u32,
455 pub end: u32,
456}
457
458#[cacheable]
459#[derive(Debug, Default, Clone, Copy, Serialize)]
460#[serde(rename_all = "camelCase")]
461pub enum ModuleArgument {
462 #[default]
463 Module,
464 RspackModule,
465}
466
467impl Display for ModuleArgument {
468 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
469 f.write_str(self.as_str())
470 }
471}
472
473impl ModuleArgument {
474 fn as_str(&self) -> &'static str {
475 match self {
476 ModuleArgument::Module => "module",
477 ModuleArgument::RspackModule => "__webpack_module__",
478 }
479 }
480}
481
482#[cacheable]
483#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize)]
484#[serde(rename_all = "camelCase")]
485pub enum ExportsArgument {
486 #[default]
487 Exports,
488 RspackExports,
489}
490
491impl Display for ExportsArgument {
492 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
493 f.write_str(self.as_str())
494 }
495}
496
497impl ExportsArgument {
498 fn as_str(&self) -> &'static str {
499 match self {
500 ExportsArgument::Exports => "exports",
501 ExportsArgument::RspackExports => "__webpack_exports__",
502 }
503 }
504}
505
506#[cacheable]
507#[derive(Debug, Default, Clone, Serialize, rspack_hash::RspackHash)]
508#[serde(rename_all = "camelCase")]
509pub struct BuildMeta {
510 #[serde(skip_serializing_if = "Option::is_none")]
511 pub strict_esm_module: Option<bool>,
512 #[serde(skip_serializing_if = "Option::is_none")]
514 pub has_top_level_await: Option<bool>,
515 #[serde(skip_serializing_if = "Option::is_none")]
516 pub esm: Option<bool>,
517 #[serde(skip_serializing_if = "Option::is_none")]
518 pub is_css_module: Option<bool>,
519 #[serde(skip_serializing_if = "Option::is_none")]
520 pub need_id_in_concatenation: Option<bool>,
521 pub exports_type: BuildMetaExportsType,
522 #[serde(skip_serializing_if = "Option::is_none")]
523 pub default_object: Option<BuildMetaDefaultObject>,
524 #[serde(skip_serializing_if = "Option::is_none")]
525 pub side_effect_free: Option<bool>,
526}
527
528impl BuildMeta {
529 pub fn strict_esm_module(&self) -> bool {
530 self.strict_esm_module.unwrap_or(false)
531 }
532
533 pub fn has_top_level_await(&self) -> bool {
534 self.has_top_level_await.unwrap_or(false)
535 }
536
537 pub fn esm(&self) -> bool {
538 self.esm.unwrap_or(false)
539 }
540
541 pub fn is_css_module(&self) -> bool {
542 self.is_css_module.unwrap_or(false)
543 }
544
545 pub fn need_id_in_concatenation(&self) -> bool {
546 self.need_id_in_concatenation.unwrap_or(false)
547 }
548
549 pub fn exports_type(&self) -> BuildMetaExportsType {
550 self.exports_type
551 }
552
553 pub fn default_object(&self) -> BuildMetaDefaultObject {
554 self.default_object.unwrap_or(BuildMetaDefaultObject::False)
555 }
556
557 pub fn side_effect_free(&self) -> bool {
558 self.side_effect_free.unwrap_or(false)
559 }
560
561 pub fn set_strict_esm_module(&mut self, value: bool) {
562 self.strict_esm_module = Some(value);
563 }
564
565 pub fn set_has_top_level_await(&mut self, value: bool) {
566 self.has_top_level_await = Some(value);
567 }
568
569 pub fn set_esm(&mut self, value: bool) {
570 self.esm = Some(value);
571 }
572
573 pub fn set_is_css_module(&mut self, value: bool) {
574 self.is_css_module = Some(value);
575 }
576
577 pub fn set_need_id_in_concatenation(&mut self, value: bool) {
578 self.need_id_in_concatenation = Some(value);
579 }
580
581 pub fn set_exports_type(&mut self, value: BuildMetaExportsType) {
582 self.exports_type = value;
583 }
584
585 pub fn clear_exports_type(&mut self) {
586 self.exports_type = BuildMetaExportsType::Unset;
587 }
588
589 pub fn set_default_object(&mut self, value: BuildMetaDefaultObject) {
590 self.default_object = Some(value);
591 }
592
593 pub fn set_side_effect_free(&mut self, value: bool) {
594 self.side_effect_free = Some(value);
595 }
596
597 pub fn with_exports_type(mut self, value: BuildMetaExportsType) -> Self {
598 self.set_exports_type(value);
599 self
600 }
601
602 pub fn with_default_object(mut self, value: BuildMetaDefaultObject) -> Self {
603 self.set_default_object(value);
604 self
605 }
606}
607
608impl RspackHash for BuildMetaExportsType {
609 fn hash(&self, state: &mut RspackHasher) {
610 if matches!(self, BuildMetaExportsType::Unset) {
611 return;
612 }
613 self.as_str().hash(state);
614 }
615}
616
617impl RspackHash for ExportsType {
618 fn hash(&self, state: &mut RspackHasher) {
619 self.as_str().hash(state);
620 }
621}
622
623impl RspackHash for BuildMetaDefaultObject {
624 fn hash(&self, state: &mut RspackHasher) {
625 self.as_str().hash(state);
626 }
627}
628
629impl RspackHash for ModuleArgument {
630 fn hash(&self, state: &mut RspackHasher) {
631 self.as_str().hash(state);
632 }
633}
634
635impl RspackHash for ExportsArgument {
636 fn hash(&self, state: &mut RspackHasher) {
637 self.as_str().hash(state);
638 }
639}
640
641#[derive(Debug)]
643pub struct BuildResult {
644 pub module: BoxModule,
645 pub dependencies: Vec<BoxDependency>,
647 pub blocks: Vec<Box<AsyncDependenciesBlock>>,
648 pub optimization_bailouts: Vec<OptimizationBailoutItem>,
649}
650
651#[cacheable]
652#[derive(Debug, Default, Clone)]
653pub struct FactoryMeta {
654 pub side_effect_free: Option<bool>,
655}
656
657pub type ModuleIdentifier = Identifier;
658pub type ResourceIdentifier = Identifier;
659
660#[derive(Debug)]
661pub struct ModuleCodeGenerationContext<'a> {
662 pub compilation: &'a Compilation,
663 pub runtime: Option<&'a RuntimeSpec>,
664 pub concatenation_scope: Option<ConcatenationScope>,
665 pub runtime_template: &'a mut ModuleCodeTemplate,
666}
667
668#[cacheable_dyn]
669#[async_trait]
670pub trait Module:
671 Debug
672 + Send
673 + Sync
674 + Any
675 + AsAny
676 + Identifiable
677 + DependenciesBlock
678 + Diagnosable
679 + ModuleSourceMapConfig
680{
681 fn module_type(&self) -> &ModuleType;
683
684 fn source_types(&self, module_graph: &ModuleGraph) -> &[SourceType];
686
687 fn source(&self) -> Option<&BoxSource>;
690
691 fn readable_identifier(&self, _context: &Context) -> Cow<'_, str>;
693
694 fn size(&self, source_type: Option<&SourceType>, compilation: Option<&Compilation>) -> f64;
697
698 async fn build(
701 self: Box<Self>,
702 _build_context: BuildContext,
703 _compilation: Option<&Compilation>,
704 ) -> Result<BuildResult>;
705
706 fn factory_meta(&self) -> Option<&FactoryMeta>;
707
708 fn set_factory_meta(&mut self, factory_meta: FactoryMeta);
709
710 fn build_info(&self) -> &BuildInfo;
711
712 fn build_info_mut(&mut self) -> &mut BuildInfo;
713
714 fn build_meta(&self) -> &BuildMeta;
715
716 fn build_meta_mut(&mut self) -> &mut BuildMeta;
717
718 fn get_exports_argument(&self) -> ExportsArgument {
719 self.build_info().exports_argument
720 }
721
722 fn get_module_argument(&self) -> ModuleArgument {
723 self.build_info().module_argument
724 }
725
726 fn get_exports_type(
727 &self,
728 module_graph: &ModuleGraph,
729 module_graph_cache: &ModuleGraphCacheArtifact,
730 exports_info_artifact: &ExportsInfoArtifact,
731 strict: bool,
732 ) -> ExportsType {
733 module_graph_cache.cached_get_exports_type((self.identifier(), strict), || {
734 get_exports_type_impl(
735 self.identifier(),
736 self.build_meta(),
737 module_graph,
738 exports_info_artifact,
739 strict,
740 )
741 })
742 }
743
744 fn get_strict_esm_module(&self) -> bool {
745 self.build_meta().strict_esm_module()
746 }
747
748 async fn code_generation(
755 &self,
756 _code_generation_context: &mut ModuleCodeGenerationContext,
757 ) -> Result<CodeGenerationResult>;
758
759 fn name_for_condition(&self) -> Option<Box<str>> {
761 None
763 }
764
765 async fn get_runtime_hash(
770 &self,
771 compilation: &Compilation,
772 runtime: Option<&RuntimeSpec>,
773 ) -> Result<RspackHashDigest>;
774
775 fn lib_ident(&self, _options: LibIdentOptions) -> Option<Cow<'_, str>> {
776 None
778 }
779
780 fn get_code_generation_dependencies(&self) -> Option<&[BoxModuleDependency]> {
785 None
786 }
787
788 fn get_presentational_dependencies(&self) -> Option<&[BoxDependencyTemplate]> {
789 None
790 }
791
792 fn concatenation_scope_info_mode(&self) -> ConcatenationScopeInfoMode {
793 ConcatenationScopeInfoMode::Unsupported
794 }
795
796 fn get_concatenation_bailout_reason(
797 &self,
798 _mg: &ModuleGraph,
799 _cg: &ChunkGraph,
800 ) -> Option<Cow<'static, str>> {
801 Some(
802 format!(
803 "Module Concatenation is not implemented for {}",
804 self.module_type()
805 )
806 .into(),
807 )
808 }
809
810 fn get_resolve_options(&self) -> Option<Arc<Resolve>> {
814 None
815 }
816
817 fn get_context(&self) -> Option<Box<Context>> {
818 None
819 }
820
821 fn get_layer(&self) -> Option<&ModuleLayer> {
822 None
823 }
824
825 fn chunk_condition(&self, _chunk_key: &ChunkUkey, _compilation: &Compilation) -> Option<bool> {
826 None
827 }
828
829 fn get_side_effects_connection_state(
830 &self,
831 _module_graph: &ModuleGraph,
832 _module_graph_cache: &ModuleGraphCacheArtifact,
833 _side_effects_state_artifact: &SideEffectsStateArtifact,
834 _module_chain: &mut IdentifierSet,
835 _connection_state_cache: &mut IdentifierMap<ConnectionState>,
836 ) -> ConnectionState {
837 ConnectionState::Active(true)
838 }
839
840 fn need_build(&self, value_cache_version: &ValueCacheVersions) -> bool {
841 let build_info = self.build_info();
842 !build_info.cacheable
843 || value_cache_version.has_diff(&build_info.value_dependencies)
844 || self.diagnostics().iter().any(|item| item.is_error())
845 }
846
847 fn need_id(&self) -> bool {
848 true
849 }
850}
851
852fn get_exports_type_impl(
853 identifier: ModuleIdentifier,
854 build_meta: &BuildMeta,
855 mg: &ModuleGraph,
856 exports_info_artifact: &ExportsInfoArtifact,
857 strict: bool,
858) -> ExportsType {
859 let export_type = build_meta.exports_type();
860 let default_object = build_meta.default_object();
861 match export_type {
862 BuildMetaExportsType::Flagged => {
863 if strict {
864 ExportsType::DefaultWithNamed
865 } else {
866 ExportsType::Namespace
867 }
868 }
869 BuildMetaExportsType::Namespace => ExportsType::Namespace,
870 BuildMetaExportsType::Default => match default_object {
871 BuildMetaDefaultObject::Redirect => ExportsType::DefaultWithNamed,
872 BuildMetaDefaultObject::RedirectWarn => {
873 if strict {
874 ExportsType::DefaultOnly
875 } else {
876 ExportsType::DefaultWithNamed
877 }
878 }
879 BuildMetaDefaultObject::False => ExportsType::DefaultOnly,
880 },
881 BuildMetaExportsType::Dynamic => {
882 if strict {
883 ExportsType::DefaultWithNamed
884 } else {
885 fn handle_default(default_object: BuildMetaDefaultObject) -> ExportsType {
886 match default_object {
887 BuildMetaDefaultObject::Redirect => ExportsType::DefaultWithNamed,
888 BuildMetaDefaultObject::RedirectWarn => ExportsType::DefaultWithNamed,
889 _ => ExportsType::DefaultOnly,
890 }
891 }
892
893 let name = Atom::from("__esModule");
894 let exports_info = exports_info_artifact.get_exports_info_optional(&identifier);
895 if let Some(export_info) = exports_info.as_ref().map(|info| {
896 info
897 .as_data(exports_info_artifact)
898 .get_read_only_export_info(&name)
899 }) {
900 if matches!(export_info.provided(), Some(ExportProvided::NotProvided)) {
901 handle_default(default_object)
902 } else {
903 let Some(GetTargetResult::Target(target)) = get_target(
904 export_info,
905 mg,
906 exports_info_artifact,
907 &|_| true,
908 &mut Default::default(),
909 ) else {
910 return ExportsType::Dynamic;
911 };
912 if target
913 .export
914 .and_then(|t| {
915 if t.len() == 1 {
916 t.first().cloned()
917 } else {
918 None
919 }
920 })
921 .is_some_and(|v| v == "__esModule")
922 {
923 let Some(target_exports_type) = mg
924 .module_by_identifier(&target.module)
925 .map(|m| m.build_meta().exports_type())
926 else {
927 return ExportsType::Dynamic;
928 };
929 match target_exports_type {
930 BuildMetaExportsType::Flagged | BuildMetaExportsType::Namespace => {
931 ExportsType::Namespace
932 }
933 BuildMetaExportsType::Default => handle_default(default_object),
934 _ => ExportsType::Dynamic,
935 }
936 } else {
937 ExportsType::Dynamic
938 }
939 }
940 } else {
941 ExportsType::DefaultWithNamed
942 }
943 }
944 }
945 BuildMetaExportsType::Unset => {
947 if strict {
948 ExportsType::DefaultWithNamed
949 } else {
950 ExportsType::Dynamic
951 }
952 }
953 }
954}
955
956pub fn module_update_hash(
957 module: &dyn Module,
958 hasher: &mut RspackHasher,
959 compilation: &Compilation,
960 runtime: Option<&RuntimeSpec>,
961) {
962 let chunk_graph = &compilation.build_chunk_graph_artifact.chunk_graph;
963 write_u64_hex(
964 chunk_graph.get_module_graph_hash(module, compilation, runtime),
965 hasher,
966 );
967 if let Some(deps) = module.get_presentational_dependencies() {
968 for dep in deps {
969 dep.update_hash(hasher, compilation, runtime);
970 }
971 }
972 dependencies_block_update_hash(
973 module.get_dependencies(),
974 module.get_blocks(),
975 hasher,
976 compilation,
977 runtime,
978 );
979}
980
981pub trait ModuleExt {
982 fn boxed(self) -> BoxModule;
983}
984
985impl<T: Module> ModuleExt for T {
986 fn boxed(self) -> BoxModule {
987 BoxModule(Box::new(self))
988 }
989}
990
991#[cacheable(with=AsInner)]
993#[repr(transparent)]
994pub struct BoxModule(Box<dyn Module>);
995
996impl BoxModule {
997 pub fn new(module: Box<dyn Module>) -> Self {
999 BoxModule(module)
1000 }
1001
1002 pub async fn build(
1003 self,
1004 build_context: BuildContext,
1005 compilation: Option<&Compilation>,
1006 ) -> Result<BuildResult> {
1007 self.0.build(build_context, compilation).await
1008 }
1009}
1010
1011impl AsInnerConverter for BoxModule {
1012 type Inner = Box<dyn Module>;
1013
1014 fn to_inner(&self) -> &Self::Inner {
1015 &self.0
1016 }
1017
1018 fn from_inner(data: Self::Inner) -> Self {
1019 BoxModule(data)
1020 }
1021}
1022
1023impl std::ops::Deref for BoxModule {
1024 type Target = Box<dyn Module>;
1025
1026 fn deref(&self) -> &Self::Target {
1027 &self.0
1028 }
1029}
1030
1031impl std::ops::DerefMut for BoxModule {
1032 fn deref_mut(&mut self) -> &mut Self::Target {
1033 &mut self.0
1034 }
1035}
1036
1037impl From<Box<dyn Module>> for BoxModule {
1038 fn from(inner: Box<dyn Module>) -> Self {
1039 BoxModule(inner)
1040 }
1041}
1042
1043impl AsRef<dyn Module> for BoxModule {
1044 fn as_ref(&self) -> &dyn Module {
1045 self.0.as_ref()
1046 }
1047}
1048
1049impl AsMut<dyn Module> for BoxModule {
1050 fn as_mut(&mut self) -> &mut dyn Module {
1051 self.0.as_mut()
1052 }
1053}
1054
1055impl Debug for BoxModule {
1056 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1057 self.0.fmt(f)
1058 }
1059}
1060
1061impl Identifiable for BoxModule {
1062 fn identifier(&self) -> Identifier {
1065 self.0.as_ref().identifier()
1066 }
1067}
1068
1069impl dyn Module {
1070 pub fn downcast_ref<T: Module + Any>(&self) -> Option<&T> {
1071 self.as_any().downcast_ref::<T>()
1072 }
1073
1074 pub fn downcast_mut<T: Module + Any>(&mut self) -> Option<&mut T> {
1075 self.as_any_mut().downcast_mut::<T>()
1076 }
1077}
1078
1079#[macro_export]
1080macro_rules! impl_module_meta_info {
1081 () => {
1082 fn factory_meta(&self) -> Option<&$crate::FactoryMeta> {
1083 self.factory_meta.as_ref()
1084 }
1085
1086 fn set_factory_meta(&mut self, v: $crate::FactoryMeta) {
1087 self.factory_meta = Some(v);
1088 }
1089
1090 fn build_info(&self) -> &$crate::BuildInfo {
1091 &self.build_info
1092 }
1093
1094 fn build_info_mut(&mut self) -> &mut $crate::BuildInfo {
1095 &mut self.build_info
1096 }
1097
1098 fn build_meta(&self) -> &$crate::BuildMeta {
1099 &self.build_meta
1100 }
1101
1102 fn build_meta_mut(&mut self) -> &mut $crate::BuildMeta {
1103 &mut self.build_meta
1104 }
1105 };
1106}
1107
1108macro_rules! impl_module_downcast_helpers {
1109 ($ty:ty, $ident:ident) => {
1110 impl dyn Module {
1111 ::paste::paste! {
1112 pub fn [<as_ $ident>](&self) -> Option<&$ty> {
1113 self.as_any().downcast_ref::<$ty>()
1114 }
1115
1116 pub fn [<as_ $ident _mut>](&mut self) -> Option<&mut $ty> {
1117 self.as_any_mut().downcast_mut::<$ty>()
1118 }
1119
1120 pub fn [<try_as_ $ident>](&self) -> Result<&$ty> {
1121 self.[<as_ $ident>]().ok_or_else(|| {
1122 ::rspack_error::error!(
1123 "Failed to cast module to a {}",
1124 stringify!($ty)
1125 )
1126 })
1127 }
1128
1129 pub fn [<try_as_ $ident _mut>](&mut self) -> Result<&mut $ty> {
1130 self.[<as_ $ident _mut>]().ok_or_else(|| {
1131 ::rspack_error::error!(
1132 "Failed to cast module to a {}",
1133 stringify!($ty)
1134 )
1135 })
1136 }
1137 }
1138 }
1139 };
1140}
1141
1142impl_module_downcast_helpers!(NormalModule, normal_module);
1143impl_module_downcast_helpers!(RawModule, raw_module);
1144impl_module_downcast_helpers!(ContextModule, context_module);
1145impl_module_downcast_helpers!(ExternalModule, external_module);
1146impl_module_downcast_helpers!(SelfModule, self_module);
1147impl_module_downcast_helpers!(ConcatenatedModule, concatenated_module);
1148
1149pub struct LibIdentOptions<'me> {
1150 pub context: &'me str,
1151}
1152
1153#[cfg(test)]
1154mod test {
1155 use std::borrow::Cow;
1156
1157 use rspack_cacheable::cacheable;
1158 use rspack_collections::{Identifiable, Identifier};
1159 use rspack_error::{Result, impl_empty_diagnosable_trait};
1160 use rspack_hash::RspackHashDigest;
1161 use rspack_sources::BoxSource;
1162 use rspack_util::source_map::{ModuleSourceMapConfig, SourceMapKind};
1163
1164 use super::{BoxModule, Module};
1165 use crate::{
1166 AsyncDependenciesBlockIdentifier, BuildContext, BuildResult, CodeGenerationResult, Compilation,
1167 Context, DependenciesBlock, DependencyId, ModuleCodeGenerationContext, ModuleExt, ModuleGraph,
1168 ModuleType, RuntimeSpec, SourceType,
1169 };
1170
1171 #[cacheable]
1172 #[derive(Debug)]
1173 struct RawModule(String);
1174
1175 #[cacheable]
1176 #[derive(Debug)]
1177 struct ExternalModule(String);
1178
1179 macro_rules! impl_noop_trait_module_type {
1180 ($ident: ident) => {
1181 impl Identifiable for $ident {
1182 fn identifier(&self) -> Identifier {
1183 self.0.clone().into()
1184 }
1185 }
1186
1187 impl_empty_diagnosable_trait!($ident);
1188
1189 impl DependenciesBlock for $ident {
1190 fn add_block_id(&mut self, _: AsyncDependenciesBlockIdentifier) {
1191 unreachable!()
1192 }
1193
1194 fn get_blocks(&self) -> &[AsyncDependenciesBlockIdentifier] {
1195 unreachable!()
1196 }
1197
1198 fn add_dependency_id(&mut self, _: DependencyId) {
1199 unreachable!()
1200 }
1201
1202 fn remove_dependency_id(&mut self, _: DependencyId) {
1203 unreachable!()
1204 }
1205
1206 fn get_dependencies(&self) -> &[DependencyId] {
1207 unreachable!()
1208 }
1209 }
1210
1211 #[::rspack_cacheable::cacheable_dyn]
1212 #[::async_trait::async_trait]
1213 impl Module for $ident {
1214 fn module_type(&self) -> &ModuleType {
1215 unreachable!()
1216 }
1217
1218 fn source_types(&self, _module_graph: &ModuleGraph) -> &[SourceType] {
1219 unreachable!()
1220 }
1221
1222 fn source(&self) -> Option<&BoxSource> {
1223 unreachable!()
1224 }
1225
1226 fn size(
1227 &self,
1228 _source_type: Option<&SourceType>,
1229 _compilation: Option<&Compilation>,
1230 ) -> f64 {
1231 unreachable!()
1232 }
1233
1234 fn readable_identifier(&self, _context: &Context) -> Cow<'_, str> {
1235 self.0.clone().into()
1236 }
1237
1238 async fn build(
1239 self: Box<Self>,
1240 _build_context: BuildContext,
1241 _compilation: Option<&Compilation>,
1242 ) -> Result<BuildResult> {
1243 unreachable!()
1244 }
1245
1246 async fn get_runtime_hash(
1247 &self,
1248 _compilation: &Compilation,
1249 _runtime: Option<&RuntimeSpec>,
1250 ) -> Result<RspackHashDigest> {
1251 unreachable!()
1252 }
1253
1254 async fn code_generation(
1255 &self,
1256 _code_generation_context: &mut ModuleCodeGenerationContext,
1257 ) -> Result<CodeGenerationResult> {
1258 unreachable!()
1259 }
1260
1261 fn factory_meta(&self) -> Option<&crate::FactoryMeta> {
1262 unreachable!()
1263 }
1264
1265 fn build_info(&self) -> &crate::BuildInfo {
1266 unreachable!()
1267 }
1268
1269 fn build_info_mut(&mut self) -> &mut crate::BuildInfo {
1270 unreachable!()
1271 }
1272
1273 fn build_meta(&self) -> &crate::BuildMeta {
1274 unreachable!()
1275 }
1276
1277 fn build_meta_mut(&mut self) -> &mut crate::BuildMeta {
1278 unreachable!()
1279 }
1280
1281 fn set_factory_meta(&mut self, _: crate::FactoryMeta) {
1282 unreachable!()
1283 }
1284 }
1285
1286 impl ModuleSourceMapConfig for $ident {
1287 fn get_source_map_kind(&self) -> &SourceMapKind {
1288 unreachable!()
1289 }
1290 fn set_source_map_kind(&mut self, _source_map: SourceMapKind) {
1291 unreachable!()
1292 }
1293 }
1294 };
1295 }
1296
1297 impl_noop_trait_module_type!(RawModule);
1298 impl_noop_trait_module_type!(ExternalModule);
1299
1300 #[test]
1301 fn should_downcast_successfully() {
1302 let a: BoxModule = ExternalModule(String::from("a")).boxed();
1303 let b: BoxModule = RawModule(String::from("a")).boxed();
1304
1305 assert!(a.downcast_ref::<ExternalModule>().is_some());
1306 assert!(b.downcast_ref::<RawModule>().is_some());
1307
1308 let a = a.as_ref();
1309 let b = b.as_ref();
1310 assert!(a.downcast_ref::<ExternalModule>().is_some());
1311 assert!(b.downcast_ref::<RawModule>().is_some());
1312 }
1313}