1use std::{
2 any::Any,
3 borrow::Cow,
4 fmt::{Debug, Display, Formatter},
5 hash::Hash,
6 sync::Arc,
7};
8
9use async_trait::async_trait;
10use json::JsonValue;
11use rspack_cacheable::{
12 cacheable, cacheable_dyn,
13 with::{AsCacheable, AsInner, AsInnerConverter, AsMap, AsOption, AsPreset, AsVec},
14};
15use rspack_collections::{Identifiable, Identifier, IdentifierMap, IdentifierSet};
16use rspack_error::{Diagnosable, Result};
17use rspack_fs::ReadableFileSystem;
18use rspack_hash::RspackHashDigest;
19use rspack_paths::ArcPathSet;
20use rspack_sources::BoxSource;
21use rspack_util::{
22 atom::Atom,
23 ext::{AsAny, DynHash},
24 fx_hash::{FxIndexMap, FxIndexSet},
25 source_map::ModuleSourceMapConfig,
26};
27use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
28use serde::Serialize;
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 ConnectionState, Context, ContextModule, DependenciesBlock, DependencyId, ExportProvided,
36 ExportsInfoArtifact, ExternalModule, GetTargetResult, ModuleCodeTemplate, ModuleGraph,
37 ModuleGraphCacheArtifact, ModuleLayer, ModuleType, NormalModule, OptimizationBailoutItem,
38 RawModule, Resolve, ResolverFactory, RuntimeSpec, SelfModule, SharedPluginDriver,
39 SideEffectsStateArtifact, SourceType, concatenated_module::ConcatenatedModule,
40 dependencies_block::dependencies_block_update_hash, get_target,
41 value_cache_versions::ValueCacheVersions,
42};
43
44pub struct BuildContext {
45 pub compiler_id: CompilerId,
46 pub compilation_id: CompilationId,
47 pub compiler_options: Arc<CompilerOptions>,
48 pub resolver_factory: Arc<ResolverFactory>,
49 pub runtime_template: ModuleCodeTemplate,
50 pub plugin_driver: SharedPluginDriver,
51 pub fs: Arc<dyn ReadableFileSystem>,
52}
53
54#[cacheable]
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
56pub enum RscModuleType {
57 ServerEntry,
66 Server,
67 Client,
68}
69
70#[cacheable]
71#[derive(Debug, Clone)]
72pub struct RscMeta {
73 pub module_type: RscModuleType,
74
75 #[cacheable(with=AsVec<AsPreset>)]
76 pub server_refs: Vec<Wtf8Atom>,
77
78 #[cacheable(with=AsVec<AsPreset>)]
79 pub client_refs: Vec<Wtf8Atom>,
80
81 pub import_meta_rsc: bool,
87
88 pub is_cjs: bool,
89
90 #[cacheable(with=AsMap<AsPreset, AsPreset>)]
91 pub action_ids: FxIndexMap<Atom, Atom>,
92}
93
94#[cacheable]
95#[derive(Debug, Clone)]
96pub enum CanonicalizedDataUrlOption {
97 Source,
98 Bytes,
99 Asset(bool),
100}
101
102impl CanonicalizedDataUrlOption {
103 pub fn is_source(&self) -> bool {
104 matches!(self, Self::Source)
105 }
106
107 pub fn is_bytes(&self) -> bool {
108 matches!(self, Self::Bytes)
109 }
110
111 pub fn is_inline(&self) -> bool {
112 matches!(self, Self::Asset(true))
113 }
114
115 pub fn is_resource(&self) -> bool {
116 matches!(self, Self::Asset(false))
117 }
118}
119
120#[cacheable]
121#[derive(Debug, Clone, PartialEq, Eq, Hash)]
122pub struct CssExport {
123 pub ident: String,
124 pub from: Option<String>,
125 pub id: Option<DependencyId>,
126 pub orig_name: String,
127}
128
129pub type CssExports = FxIndexMap<String, FxIndexSet<CssExport>>;
130
131#[cacheable]
132#[derive(Debug, Clone)]
133pub struct IsolatedDts {
134 pub resource_path: String,
135 pub code: String,
136}
137
138#[cacheable]
139#[derive(Debug, Clone)]
140pub struct BuildInfo {
141 pub cacheable: bool,
143 pub hash: Option<RspackHashDigest>,
144 pub strict: bool,
145 pub module_argument: ModuleArgument,
146 pub exports_argument: ExportsArgument,
147 pub file_dependencies: ArcPathSet,
148 pub context_dependencies: ArcPathSet,
149 pub missing_dependencies: ArcPathSet,
150 pub build_dependencies: ArcPathSet,
151 pub value_dependencies: HashMap<String, String>,
152 #[cacheable(with=AsVec<AsPreset>)]
153 pub esm_named_exports: HashSet<Atom>,
154 pub all_star_exports: Vec<DependencyId>,
155 pub need_create_require: bool,
156 #[cacheable(with=AsOption<AsPreset>)]
157 pub json_data: Option<JsonValue>,
158 pub asset_data_url: Option<CanonicalizedDataUrlOption>,
159 #[cacheable(with=AsOption<AsMap<AsCacheable, AsVec>>)]
160 pub css_exports: Option<CssExports>,
161 pub css_local_names: Option<HashMap<String, String>>,
162 #[cacheable(with=AsOption<AsVec<AsPreset>>)]
163 pub side_effects_free: Option<HashSet<Atom>>,
164 #[cacheable(with=AsOption<AsVec<AsPreset>>)]
165 pub top_level_declarations: Option<HashSet<Atom>>,
166 pub module_concatenation_bailout: Option<String>,
167 pub assets: BindingCell<HashMap<String, CompilationAsset>>,
168 pub module: bool,
169 pub inline_exports: bool,
170 pub collected_typescript_info: Option<CollectedTypeScriptInfo>,
171 pub rsc: Option<RscMeta>,
172 pub isolated_dts: Option<IsolatedDts>,
173 #[cacheable(with=AsPreset)]
176 pub extras: serde_json::Map<String, serde_json::Value>,
177 #[cacheable(with=AsVec)]
178 pub deferred_pure_checks: HashSet<DeferredPureCheck>,
179}
180
181impl Default for BuildInfo {
182 fn default() -> Self {
183 Self {
184 cacheable: true,
185 hash: None,
186 strict: false,
187 module_argument: Default::default(),
188 exports_argument: Default::default(),
189 file_dependencies: ArcPathSet::default(),
190 context_dependencies: ArcPathSet::default(),
191 missing_dependencies: ArcPathSet::default(),
192 build_dependencies: ArcPathSet::default(),
193 value_dependencies: HashMap::default(),
194 esm_named_exports: HashSet::default(),
195 all_star_exports: Vec::default(),
196 need_create_require: false,
197 json_data: None,
198 asset_data_url: None,
199 css_exports: None,
200 css_local_names: None,
201 side_effects_free: None,
202 top_level_declarations: None,
203 module_concatenation_bailout: None,
204 assets: Default::default(),
205 module: false,
206 inline_exports: false,
207 collected_typescript_info: None,
208 rsc: None,
209 isolated_dts: None,
210 extras: Default::default(),
211 deferred_pure_checks: HashSet::default(),
212 }
213 }
214}
215
216#[cacheable]
217#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, Serialize)]
218#[serde(rename_all = "camelCase")]
219pub enum BuildMetaExportsType {
220 #[default]
221 Unset,
222 Default,
223 Namespace,
224 Flagged,
225 Dynamic,
226}
227
228impl Display for BuildMetaExportsType {
229 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
230 let d = match self {
231 BuildMetaExportsType::Unset => "unknown exports (runtime-defined)",
232 BuildMetaExportsType::Default => "default exports",
233 BuildMetaExportsType::Namespace => "namespace exports",
234 BuildMetaExportsType::Flagged => "flagged exports",
235 BuildMetaExportsType::Dynamic => "dynamic exports",
236 };
237
238 f.write_str(d)
239 }
240}
241
242#[derive(Debug, Clone, Copy, Hash)]
243pub enum ExportsType {
244 DefaultOnly,
245 Namespace,
246 DefaultWithNamed,
247 Dynamic,
248}
249
250#[cacheable]
251#[derive(Debug, Default, Clone, Copy, Hash, Serialize)]
252#[serde(rename_all = "camelCase")]
253pub enum BuildMetaDefaultObject {
254 #[default]
255 False,
256 Redirect,
257 RedirectWarn,
258}
259
260#[cacheable]
261#[derive(Debug, Clone, PartialEq, Eq, Hash)]
262pub struct DeferredPureCheck {
263 #[cacheable(with=AsPreset)]
264 pub atom: Atom,
265 pub dep_id: DependencyId,
266 pub start: u32,
267 pub end: u32,
268}
269
270#[cacheable]
271#[derive(Debug, Default, Clone, Copy, Hash, Serialize)]
272#[serde(rename_all = "camelCase")]
273pub enum ModuleArgument {
274 #[default]
275 Module,
276 RspackModule,
277}
278
279#[cacheable]
280#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, Serialize)]
281#[serde(rename_all = "camelCase")]
282pub enum ExportsArgument {
283 #[default]
284 Exports,
285 RspackExports,
286}
287
288#[cacheable]
289#[derive(Debug, Default, Clone, Hash, Serialize)]
290#[serde(rename_all = "camelCase")]
291pub struct BuildMeta {
292 pub strict_esm_module: bool,
293 pub has_top_level_await: bool,
295 pub esm: bool,
296 pub exports_type: BuildMetaExportsType,
297 pub default_object: BuildMetaDefaultObject,
298 #[serde(skip_serializing_if = "Option::is_none")]
299 pub side_effect_free: Option<bool>,
300}
301
302#[derive(Debug)]
304pub struct BuildResult {
305 pub module: BoxModule,
306 pub dependencies: Vec<BoxDependency>,
308 pub blocks: Vec<Box<AsyncDependenciesBlock>>,
309 pub optimization_bailouts: Vec<OptimizationBailoutItem>,
310}
311
312#[cacheable]
313#[derive(Debug, Default, Clone)]
314pub struct FactoryMeta {
315 pub side_effect_free: Option<bool>,
316}
317
318pub type ModuleIdentifier = Identifier;
319pub type ResourceIdentifier = Identifier;
320
321#[derive(Debug)]
322pub struct ModuleCodeGenerationContext<'a> {
323 pub compilation: &'a Compilation,
324 pub runtime: Option<&'a RuntimeSpec>,
325 pub concatenation_scope: Option<ConcatenationScope>,
326 pub runtime_template: &'a mut ModuleCodeTemplate,
327}
328
329#[cacheable_dyn]
330#[async_trait]
331pub trait Module:
332 Debug
333 + Send
334 + Sync
335 + Any
336 + AsAny
337 + Identifiable
338 + DependenciesBlock
339 + Diagnosable
340 + ModuleSourceMapConfig
341{
342 fn module_type(&self) -> &ModuleType;
344
345 fn source_types(&self, module_graph: &ModuleGraph) -> &[SourceType];
347
348 fn source(&self) -> Option<&BoxSource>;
351
352 fn readable_identifier(&self, _context: &Context) -> Cow<'_, str>;
354
355 fn size(&self, source_type: Option<&SourceType>, compilation: Option<&Compilation>) -> f64;
358
359 async fn build(
362 self: Box<Self>,
363 _build_context: BuildContext,
364 _compilation: Option<&Compilation>,
365 ) -> Result<BuildResult>;
366
367 fn factory_meta(&self) -> Option<&FactoryMeta>;
368
369 fn set_factory_meta(&mut self, factory_meta: FactoryMeta);
370
371 fn build_info(&self) -> &BuildInfo;
372
373 fn build_info_mut(&mut self) -> &mut BuildInfo;
374
375 fn build_meta(&self) -> &BuildMeta;
376
377 fn build_meta_mut(&mut self) -> &mut BuildMeta;
378
379 fn get_exports_argument(&self) -> ExportsArgument {
380 self.build_info().exports_argument
381 }
382
383 fn get_module_argument(&self) -> ModuleArgument {
384 self.build_info().module_argument
385 }
386
387 fn get_exports_type(
388 &self,
389 module_graph: &ModuleGraph,
390 module_graph_cache: &ModuleGraphCacheArtifact,
391 exports_info_artifact: &ExportsInfoArtifact,
392 strict: bool,
393 ) -> ExportsType {
394 module_graph_cache.cached_get_exports_type((self.identifier(), strict), || {
395 get_exports_type_impl(
396 self.identifier(),
397 self.build_meta(),
398 module_graph,
399 exports_info_artifact,
400 strict,
401 )
402 })
403 }
404
405 fn get_strict_esm_module(&self) -> bool {
406 self.build_meta().strict_esm_module
407 }
408
409 async fn code_generation(
416 &self,
417 _code_generation_context: &mut ModuleCodeGenerationContext,
418 ) -> Result<CodeGenerationResult>;
419
420 fn name_for_condition(&self) -> Option<Box<str>> {
422 None
424 }
425
426 async fn get_runtime_hash(
431 &self,
432 compilation: &Compilation,
433 runtime: Option<&RuntimeSpec>,
434 ) -> Result<RspackHashDigest>;
435
436 fn lib_ident(&self, _options: LibIdentOptions) -> Option<Cow<'_, str>> {
437 None
439 }
440
441 fn get_code_generation_dependencies(&self) -> Option<&[BoxModuleDependency]> {
446 None
447 }
448
449 fn get_presentational_dependencies(&self) -> Option<&[BoxDependencyTemplate]> {
450 None
451 }
452
453 fn get_concatenation_bailout_reason(
454 &self,
455 _mg: &ModuleGraph,
456 _cg: &ChunkGraph,
457 ) -> Option<Cow<'static, str>> {
458 Some(
459 format!(
460 "Module Concatenation is not implemented for {}",
461 self.module_type()
462 )
463 .into(),
464 )
465 }
466
467 fn get_resolve_options(&self) -> Option<Arc<Resolve>> {
471 None
472 }
473
474 fn get_context(&self) -> Option<Box<Context>> {
475 None
476 }
477
478 fn get_layer(&self) -> Option<&ModuleLayer> {
479 None
480 }
481
482 fn chunk_condition(&self, _chunk_key: &ChunkUkey, _compilation: &Compilation) -> Option<bool> {
483 None
484 }
485
486 fn get_side_effects_connection_state(
487 &self,
488 _module_graph: &ModuleGraph,
489 _module_graph_cache: &ModuleGraphCacheArtifact,
490 _side_effects_state_artifact: &SideEffectsStateArtifact,
491 _module_chain: &mut IdentifierSet,
492 _connection_state_cache: &mut IdentifierMap<ConnectionState>,
493 ) -> ConnectionState {
494 ConnectionState::Active(true)
495 }
496
497 fn need_build(&self, value_cache_version: &ValueCacheVersions) -> bool {
498 let build_info = self.build_info();
499 !build_info.cacheable
500 || value_cache_version.has_diff(&build_info.value_dependencies)
501 || self.diagnostics().iter().any(|item| item.is_error())
502 }
503
504 fn need_id(&self) -> bool {
505 true
506 }
507}
508
509fn get_exports_type_impl(
510 identifier: ModuleIdentifier,
511 build_meta: &BuildMeta,
512 mg: &ModuleGraph,
513 exports_info_artifact: &ExportsInfoArtifact,
514 strict: bool,
515) -> ExportsType {
516 let export_type = &build_meta.exports_type;
517 let default_object = &build_meta.default_object;
518 match export_type {
519 BuildMetaExportsType::Flagged => {
520 if strict {
521 ExportsType::DefaultWithNamed
522 } else {
523 ExportsType::Namespace
524 }
525 }
526 BuildMetaExportsType::Namespace => ExportsType::Namespace,
527 BuildMetaExportsType::Default => match default_object {
528 BuildMetaDefaultObject::Redirect => ExportsType::DefaultWithNamed,
529 BuildMetaDefaultObject::RedirectWarn => {
530 if strict {
531 ExportsType::DefaultOnly
532 } else {
533 ExportsType::DefaultWithNamed
534 }
535 }
536 BuildMetaDefaultObject::False => ExportsType::DefaultOnly,
537 },
538 BuildMetaExportsType::Dynamic => {
539 if strict {
540 ExportsType::DefaultWithNamed
541 } else {
542 fn handle_default(default_object: &BuildMetaDefaultObject) -> ExportsType {
543 match default_object {
544 BuildMetaDefaultObject::Redirect => ExportsType::DefaultWithNamed,
545 BuildMetaDefaultObject::RedirectWarn => ExportsType::DefaultWithNamed,
546 _ => ExportsType::DefaultOnly,
547 }
548 }
549
550 let name = Atom::from("__esModule");
551 let exports_info = exports_info_artifact.get_exports_info_optional(&identifier);
552 if let Some(export_info) = exports_info.as_ref().map(|info| {
553 info
554 .as_data(exports_info_artifact)
555 .get_read_only_export_info(&name)
556 }) {
557 if matches!(export_info.provided(), Some(ExportProvided::NotProvided)) {
558 handle_default(default_object)
559 } else {
560 let Some(GetTargetResult::Target(target)) = get_target(
561 export_info,
562 mg,
563 exports_info_artifact,
564 &|_| true,
565 &mut Default::default(),
566 ) else {
567 return ExportsType::Dynamic;
568 };
569 if target
570 .export
571 .and_then(|t| {
572 if t.len() == 1 {
573 t.first().cloned()
574 } else {
575 None
576 }
577 })
578 .is_some_and(|v| v == "__esModule")
579 {
580 let Some(target_exports_type) = mg
581 .module_by_identifier(&target.module)
582 .map(|m| m.build_meta().exports_type)
583 else {
584 return ExportsType::Dynamic;
585 };
586 match target_exports_type {
587 BuildMetaExportsType::Flagged | BuildMetaExportsType::Namespace => {
588 ExportsType::Namespace
589 }
590 BuildMetaExportsType::Default => handle_default(default_object),
591 _ => ExportsType::Dynamic,
592 }
593 } else {
594 ExportsType::Dynamic
595 }
596 }
597 } else {
598 ExportsType::DefaultWithNamed
599 }
600 }
601 }
602 BuildMetaExportsType::Unset => {
604 if strict {
605 ExportsType::DefaultWithNamed
606 } else {
607 ExportsType::Dynamic
608 }
609 }
610 }
611}
612
613pub fn module_update_hash(
614 module: &dyn Module,
615 hasher: &mut dyn std::hash::Hasher,
616 compilation: &Compilation,
617 runtime: Option<&RuntimeSpec>,
618) {
619 let chunk_graph = &compilation.build_chunk_graph_artifact.chunk_graph;
620 chunk_graph
621 .get_module_graph_hash(module, compilation, runtime)
622 .dyn_hash(hasher);
623 if let Some(deps) = module.get_presentational_dependencies() {
624 for dep in deps {
625 dep.update_hash(hasher, compilation, runtime);
626 }
627 }
628 dependencies_block_update_hash(
629 module.get_dependencies(),
630 module.get_blocks(),
631 hasher,
632 compilation,
633 runtime,
634 );
635}
636
637pub trait ModuleExt {
638 fn boxed(self) -> BoxModule;
639}
640
641impl<T: Module> ModuleExt for T {
642 fn boxed(self) -> BoxModule {
643 BoxModule(Box::new(self))
644 }
645}
646
647#[cacheable(with=AsInner)]
649#[repr(transparent)]
650pub struct BoxModule(Box<dyn Module>);
651
652impl BoxModule {
653 pub fn new(module: Box<dyn Module>) -> Self {
655 BoxModule(module)
656 }
657
658 pub async fn build(
659 self,
660 build_context: BuildContext,
661 compilation: Option<&Compilation>,
662 ) -> Result<BuildResult> {
663 self.0.build(build_context, compilation).await
664 }
665}
666
667impl AsInnerConverter for BoxModule {
668 type Inner = Box<dyn Module>;
669
670 fn to_inner(&self) -> &Self::Inner {
671 &self.0
672 }
673
674 fn from_inner(data: Self::Inner) -> Self {
675 BoxModule(data)
676 }
677}
678
679impl std::ops::Deref for BoxModule {
680 type Target = Box<dyn Module>;
681
682 fn deref(&self) -> &Self::Target {
683 &self.0
684 }
685}
686
687impl std::ops::DerefMut for BoxModule {
688 fn deref_mut(&mut self) -> &mut Self::Target {
689 &mut self.0
690 }
691}
692
693impl From<Box<dyn Module>> for BoxModule {
694 fn from(inner: Box<dyn Module>) -> Self {
695 BoxModule(inner)
696 }
697}
698
699impl AsRef<dyn Module> for BoxModule {
700 fn as_ref(&self) -> &dyn Module {
701 self.0.as_ref()
702 }
703}
704
705impl AsMut<dyn Module> for BoxModule {
706 fn as_mut(&mut self) -> &mut dyn Module {
707 self.0.as_mut()
708 }
709}
710
711impl Debug for BoxModule {
712 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
713 self.0.fmt(f)
714 }
715}
716
717impl Identifiable for BoxModule {
718 fn identifier(&self) -> Identifier {
721 self.0.as_ref().identifier()
722 }
723}
724
725impl dyn Module {
726 pub fn downcast_ref<T: Module + Any>(&self) -> Option<&T> {
727 self.as_any().downcast_ref::<T>()
728 }
729
730 pub fn downcast_mut<T: Module + Any>(&mut self) -> Option<&mut T> {
731 self.as_any_mut().downcast_mut::<T>()
732 }
733}
734
735#[macro_export]
736macro_rules! impl_module_meta_info {
737 () => {
738 fn factory_meta(&self) -> Option<&$crate::FactoryMeta> {
739 self.factory_meta.as_ref()
740 }
741
742 fn set_factory_meta(&mut self, v: $crate::FactoryMeta) {
743 self.factory_meta = Some(v);
744 }
745
746 fn build_info(&self) -> &$crate::BuildInfo {
747 &self.build_info
748 }
749
750 fn build_info_mut(&mut self) -> &mut $crate::BuildInfo {
751 &mut self.build_info
752 }
753
754 fn build_meta(&self) -> &$crate::BuildMeta {
755 &self.build_meta
756 }
757
758 fn build_meta_mut(&mut self) -> &mut $crate::BuildMeta {
759 &mut self.build_meta
760 }
761 };
762}
763
764macro_rules! impl_module_downcast_helpers {
765 ($ty:ty, $ident:ident) => {
766 impl dyn Module {
767 ::paste::paste! {
768 pub fn [<as_ $ident>](&self) -> Option<&$ty> {
769 self.as_any().downcast_ref::<$ty>()
770 }
771
772 pub fn [<as_ $ident _mut>](&mut self) -> Option<&mut $ty> {
773 self.as_any_mut().downcast_mut::<$ty>()
774 }
775
776 pub fn [<try_as_ $ident>](&self) -> Result<&$ty> {
777 self.[<as_ $ident>]().ok_or_else(|| {
778 ::rspack_error::error!(
779 "Failed to cast module to a {}",
780 stringify!($ty)
781 )
782 })
783 }
784
785 pub fn [<try_as_ $ident _mut>](&mut self) -> Result<&mut $ty> {
786 self.[<as_ $ident _mut>]().ok_or_else(|| {
787 ::rspack_error::error!(
788 "Failed to cast module to a {}",
789 stringify!($ty)
790 )
791 })
792 }
793 }
794 }
795 };
796}
797
798impl_module_downcast_helpers!(NormalModule, normal_module);
799impl_module_downcast_helpers!(RawModule, raw_module);
800impl_module_downcast_helpers!(ContextModule, context_module);
801impl_module_downcast_helpers!(ExternalModule, external_module);
802impl_module_downcast_helpers!(SelfModule, self_module);
803impl_module_downcast_helpers!(ConcatenatedModule, concatenated_module);
804
805pub struct LibIdentOptions<'me> {
806 pub context: &'me str,
807}
808
809#[cfg(test)]
810mod test {
811 use std::borrow::Cow;
812
813 use rspack_cacheable::cacheable;
814 use rspack_collections::{Identifiable, Identifier};
815 use rspack_error::{Result, impl_empty_diagnosable_trait};
816 use rspack_hash::RspackHashDigest;
817 use rspack_sources::BoxSource;
818 use rspack_util::source_map::{ModuleSourceMapConfig, SourceMapKind};
819
820 use super::{BoxModule, Module};
821 use crate::{
822 AsyncDependenciesBlockIdentifier, BuildContext, BuildResult, CodeGenerationResult, Compilation,
823 Context, DependenciesBlock, DependencyId, ModuleCodeGenerationContext, ModuleExt, ModuleGraph,
824 ModuleType, RuntimeSpec, SourceType,
825 };
826
827 #[cacheable]
828 #[derive(Debug)]
829 struct RawModule(String);
830
831 #[cacheable]
832 #[derive(Debug)]
833 struct ExternalModule(String);
834
835 macro_rules! impl_noop_trait_module_type {
836 ($ident: ident) => {
837 impl Identifiable for $ident {
838 fn identifier(&self) -> Identifier {
839 self.0.clone().into()
840 }
841 }
842
843 impl_empty_diagnosable_trait!($ident);
844
845 impl DependenciesBlock for $ident {
846 fn add_block_id(&mut self, _: AsyncDependenciesBlockIdentifier) {
847 unreachable!()
848 }
849
850 fn get_blocks(&self) -> &[AsyncDependenciesBlockIdentifier] {
851 unreachable!()
852 }
853
854 fn add_dependency_id(&mut self, _: DependencyId) {
855 unreachable!()
856 }
857
858 fn remove_dependency_id(&mut self, _: DependencyId) {
859 unreachable!()
860 }
861
862 fn get_dependencies(&self) -> &[DependencyId] {
863 unreachable!()
864 }
865 }
866
867 #[::rspack_cacheable::cacheable_dyn]
868 #[::async_trait::async_trait]
869 impl Module for $ident {
870 fn module_type(&self) -> &ModuleType {
871 unreachable!()
872 }
873
874 fn source_types(&self, _module_graph: &ModuleGraph) -> &[SourceType] {
875 unreachable!()
876 }
877
878 fn source(&self) -> Option<&BoxSource> {
879 unreachable!()
880 }
881
882 fn size(
883 &self,
884 _source_type: Option<&SourceType>,
885 _compilation: Option<&Compilation>,
886 ) -> f64 {
887 unreachable!()
888 }
889
890 fn readable_identifier(&self, _context: &Context) -> Cow<'_, str> {
891 self.0.clone().into()
892 }
893
894 async fn build(
895 self: Box<Self>,
896 _build_context: BuildContext,
897 _compilation: Option<&Compilation>,
898 ) -> Result<BuildResult> {
899 unreachable!()
900 }
901
902 async fn get_runtime_hash(
903 &self,
904 _compilation: &Compilation,
905 _runtime: Option<&RuntimeSpec>,
906 ) -> Result<RspackHashDigest> {
907 unreachable!()
908 }
909
910 async fn code_generation(
911 &self,
912 _code_generation_context: &mut ModuleCodeGenerationContext,
913 ) -> Result<CodeGenerationResult> {
914 unreachable!()
915 }
916
917 fn factory_meta(&self) -> Option<&crate::FactoryMeta> {
918 unreachable!()
919 }
920
921 fn build_info(&self) -> &crate::BuildInfo {
922 unreachable!()
923 }
924
925 fn build_info_mut(&mut self) -> &mut crate::BuildInfo {
926 unreachable!()
927 }
928
929 fn build_meta(&self) -> &crate::BuildMeta {
930 unreachable!()
931 }
932
933 fn build_meta_mut(&mut self) -> &mut crate::BuildMeta {
934 unreachable!()
935 }
936
937 fn set_factory_meta(&mut self, _: crate::FactoryMeta) {
938 unreachable!()
939 }
940 }
941
942 impl ModuleSourceMapConfig for $ident {
943 fn get_source_map_kind(&self) -> &SourceMapKind {
944 unreachable!()
945 }
946 fn set_source_map_kind(&mut self, _source_map: SourceMapKind) {
947 unreachable!()
948 }
949 }
950 };
951 }
952
953 impl_noop_trait_module_type!(RawModule);
954 impl_noop_trait_module_type!(ExternalModule);
955
956 #[test]
957 fn should_downcast_successfully() {
958 let a: BoxModule = ExternalModule(String::from("a")).boxed();
959 let b: BoxModule = RawModule(String::from("a")).boxed();
960
961 assert!(a.downcast_ref::<ExternalModule>().is_some());
962 assert!(b.downcast_ref::<RawModule>().is_some());
963
964 let a = a.as_ref();
965 let b = b.as_ref();
966 assert!(a.downcast_ref::<ExternalModule>().is_some());
967 assert!(b.downcast_ref::<RawModule>().is_some());
968 }
969}