Skip to main content

rspack_core/
module.rs

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  /// Represents a server entry module with "use server-entry" directive.
59  ///
60  /// Transformation flow:
61  /// 1. Original module with "use server-entry" is transformed into a proxy module
62  /// 2. The proxy module (with `module_type = ServerEntry`) imports the original implementation
63  /// 3. The original implementation module may resulting in `module_type = Client`
64  ///
65  /// Note: "use server" and "use client" directives can coexist in the same file.
66  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  /// Whether this server component uses `import.meta.rspackRsc`.
83  ///
84  /// RSC client manifest collection uses this to find the module's transitive
85  /// CSS dependencies, so they can be exposed through `entryCssFiles` and
86  /// rendered by `loadCss()`.
87  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  /// Conditions inherited from parent CSS modules.
222  ///
223  /// Webpack stores the current module condition before inherited conditions.
224  /// Rspack stores inherited conditions from outermost to innermost
225  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  /// Whether the result is cacheable, i.e shared between builds.
266  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  /// Stores external fields from the JS side (Record<string, any>),
298  /// while other properties are stored in KnownBuildInfo.
299  #[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  // Must match webpack / Display (`redirect-warn`), not serde camelCase (`redirectWarn`).
427  // camelCase broke DllPlugin manifests consumed by DllReferencePlugin (see #15010).
428  #[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  // same as is_async https://github.com/webpack/webpack/blob/3919c844eca394d73ca930e4fc5506fb86e2b094/lib/Module.js#L107
513  #[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// webpack build info
642#[derive(Debug)]
643pub struct BuildResult {
644  pub module: BoxModule,
645  /// Whether the result is cacheable, i.e shared between builds.
646  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  /// Defines what kind of module this is.
682  fn module_type(&self) -> &ModuleType;
683
684  /// Defines what kind of code generation results this module can generate.
685  fn source_types(&self, module_graph: &ModuleGraph) -> &[SourceType];
686
687  /// The source of the module. This could be optional, modules like the `NormalModule` can have the corresponding source.
688  /// However, modules that is created from "nowhere" (e.g. `ExternalModule` and `MissingModule`) does not have its source.
689  fn source(&self) -> Option<&BoxSource>;
690
691  /// User readable identifier of the module.
692  fn readable_identifier(&self, _context: &Context) -> Cow<'_, str>;
693
694  /// The size of the original source, which will used as a parameter for code-splitting.
695  /// Only when calculating the size of the RuntimeModule is the Compilation depended on
696  fn size(&self, source_type: Option<&SourceType>, compilation: Option<&Compilation>) -> f64;
697
698  /// The actual build of the module, which will be called by the `Compilation`.
699  /// Build can also returns the dependencies of the module, which will be used by the `Compilation` to build the dependency graph.
700  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  /// The actual code generation of the module, which will be called by the `Compilation`.
749  /// The code generation result should not be cached as it is implemented elsewhere to
750  /// provide a universal cache mechanism (time to invalidate cache, etc.)
751  ///
752  /// Code generation will often iterate through every `source_types` given by the module
753  /// to provide multiple code generation results for different `source_type`s.
754  async fn code_generation(
755    &self,
756    _code_generation_context: &mut ModuleCodeGenerationContext,
757  ) -> Result<CodeGenerationResult>;
758
759  /// Name matched against bundle-splitting conditions.
760  fn name_for_condition(&self) -> Option<Box<str>> {
761    // Align with https://github.com/webpack/webpack/blob/8241da7f1e75c5581ba535d127fa66aeb9eb2ac8/lib/Module.js#L852
762    None
763  }
764
765  /// Update hash for cgm.hash (chunk graph module hash)
766  /// Different cgm code generation result should have different cgm.hash,
767  /// so this also accept compilation (mainly chunk graph) and runtime as args.
768  /// (Difference with `impl Hash for Module`: this is just a part for calculating cgm.hash, not for Module itself)
769  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    // Align with https://github.com/webpack/webpack/blob/4b4ca3bb53f36a5b8fc6bc1bd976ed7af161bd80/lib/Module.js#L845
777    None
778  }
779
780  /// Code generation dependencies of the module, which means the code generation of this module
781  /// depends on the code generation results of dependencies which are returned by this function.
782  /// e.g `Css` module may rely on the code generation result of `CssUrlDependency` to re-direct
783  /// the url of the referenced assets.
784  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  /// Resolve options matched by module rules.
811  /// e.g `javascript/esm` may have special resolving options like `fullySpecified`.
812  /// `css` and `css/module` may have special resolving options like `preferRelative`.
813  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    // align to undefined
946    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/// A newtype wrapper around `Box<dyn Module>` for improved type safety.
992#[cacheable(with=AsInner)]
993#[repr(transparent)]
994pub struct BoxModule(Box<dyn Module>);
995
996impl BoxModule {
997  /// Create a new BoxModule from a boxed Module trait object.
998  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  /// Uniquely identify a module. If two modules share the same module identifier, then they are considered as the same module.
1063  /// e.g `javascript/auto|<absolute-path>/index.js` and `javascript/auto|<absolute-path>/index.js` are considered as the same.
1064  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}