Skip to main content

rspack_core/
module.rs

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  /// Represents a server entry module with "use server-entry" directive.
58  ///
59  /// Transformation flow:
60  /// 1. Original module with "use server-entry" is transformed into a proxy module
61  /// 2. The proxy module (with `module_type = ServerEntry`) imports the original implementation
62  /// 3. The original implementation module may resulting in `module_type = Client`
63  ///
64  /// Note: "use server" and "use client" directives can coexist in the same file.
65  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  /// Whether this server component uses `import.meta.rspackRsc`.
82  ///
83  /// RSC client manifest collection uses this to find the module's transitive
84  /// CSS dependencies, so they can be exposed through `entryCssFiles` and
85  /// rendered by `loadCss()`.
86  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  /// Whether the result is cacheable, i.e shared between builds.
142  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  /// Stores external fields from the JS side (Record<string, any>),
174  /// while other properties are stored in KnownBuildInfo.
175  #[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  // same as is_async https://github.com/webpack/webpack/blob/3919c844eca394d73ca930e4fc5506fb86e2b094/lib/Module.js#L107
294  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// webpack build info
303#[derive(Debug)]
304pub struct BuildResult {
305  pub module: BoxModule,
306  /// Whether the result is cacheable, i.e shared between builds.
307  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  /// Defines what kind of module this is.
343  fn module_type(&self) -> &ModuleType;
344
345  /// Defines what kind of code generation results this module can generate.
346  fn source_types(&self, module_graph: &ModuleGraph) -> &[SourceType];
347
348  /// The source of the module. This could be optional, modules like the `NormalModule` can have the corresponding source.
349  /// However, modules that is created from "nowhere" (e.g. `ExternalModule` and `MissingModule`) does not have its source.
350  fn source(&self) -> Option<&BoxSource>;
351
352  /// User readable identifier of the module.
353  fn readable_identifier(&self, _context: &Context) -> Cow<'_, str>;
354
355  /// The size of the original source, which will used as a parameter for code-splitting.
356  /// Only when calculating the size of the RuntimeModule is the Compilation depended on
357  fn size(&self, source_type: Option<&SourceType>, compilation: Option<&Compilation>) -> f64;
358
359  /// The actual build of the module, which will be called by the `Compilation`.
360  /// Build can also returns the dependencies of the module, which will be used by the `Compilation` to build the dependency graph.
361  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  /// The actual code generation of the module, which will be called by the `Compilation`.
410  /// The code generation result should not be cached as it is implemented elsewhere to
411  /// provide a universal cache mechanism (time to invalidate cache, etc.)
412  ///
413  /// Code generation will often iterate through every `source_types` given by the module
414  /// to provide multiple code generation results for different `source_type`s.
415  async fn code_generation(
416    &self,
417    _code_generation_context: &mut ModuleCodeGenerationContext,
418  ) -> Result<CodeGenerationResult>;
419
420  /// Name matched against bundle-splitting conditions.
421  fn name_for_condition(&self) -> Option<Box<str>> {
422    // Align with https://github.com/webpack/webpack/blob/8241da7f1e75c5581ba535d127fa66aeb9eb2ac8/lib/Module.js#L852
423    None
424  }
425
426  /// Update hash for cgm.hash (chunk graph module hash)
427  /// Different cgm code generation result should have different cgm.hash,
428  /// so this also accept compilation (mainly chunk graph) and runtime as args.
429  /// (Difference with `impl Hash for Module`: this is just a part for calculating cgm.hash, not for Module itself)
430  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    // Align with https://github.com/webpack/webpack/blob/4b4ca3bb53f36a5b8fc6bc1bd976ed7af161bd80/lib/Module.js#L845
438    None
439  }
440
441  /// Code generation dependencies of the module, which means the code generation of this module
442  /// depends on the code generation results of dependencies which are returned by this function.
443  /// e.g `Css` module may rely on the code generation result of `CssUrlDependency` to re-direct
444  /// the url of the referenced assets.
445  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  /// Resolve options matched by module rules.
468  /// e.g `javascript/esm` may have special resolving options like `fullySpecified`.
469  /// `css` and `css/module` may have special resolving options like `preferRelative`.
470  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    // align to undefined
603    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/// A newtype wrapper around `Box<dyn Module>` for improved type safety.
648#[cacheable(with=AsInner)]
649#[repr(transparent)]
650pub struct BoxModule(Box<dyn Module>);
651
652impl BoxModule {
653  /// Create a new BoxModule from a boxed Module trait object.
654  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  /// Uniquely identify a module. If two modules share the same module identifier, then they are considered as the same module.
719  /// e.g `javascript/auto|<absolute-path>/index.js` and `javascript/auto|<absolute-path>/index.js` are considered as the same.
720  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}