rspack_plugin_mf/sharing/
consume_shared_module.rs

1use std::borrow::Cow;
2
3use async_trait::async_trait;
4use rspack_cacheable::{cacheable, cacheable_dyn, with::Unsupported};
5use rspack_collections::{Identifiable, Identifier};
6use rspack_core::{
7  AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, BoxDependency, BuildContext, BuildInfo,
8  BuildMeta, BuildResult, CodeGenerationResult, Compilation, ConcatenationScope, Context,
9  DependenciesBlock, DependencyId, FactoryMeta, LibIdentOptions, Module, ModuleGraph,
10  ModuleIdentifier, ModuleType, RuntimeGlobals, RuntimeSpec, SourceType, async_module_factory,
11  impl_module_meta_info, impl_source_map_config, module_update_hash, rspack_sources::BoxSource,
12  sync_module_factory,
13};
14use rspack_error::{Result, impl_empty_diagnosable_trait};
15use rspack_hash::{RspackHash, RspackHashDigest};
16use rspack_util::{ext::DynHash, source_map::SourceMapKind};
17
18use super::{
19  consume_shared_fallback_dependency::ConsumeSharedFallbackDependency,
20  consume_shared_runtime_module::CodeGenerationDataConsumeShared,
21};
22use crate::{ConsumeOptions, utils::json_stringify};
23
24#[impl_source_map_config]
25#[cacheable]
26#[derive(Debug)]
27pub struct ConsumeSharedModule {
28  #[cacheable(with=Unsupported)]
29  blocks: Vec<AsyncDependenciesBlockIdentifier>,
30  dependencies: Vec<DependencyId>,
31  identifier: ModuleIdentifier,
32  lib_ident: String,
33  readable_identifier: String,
34  context: Context,
35  options: ConsumeOptions,
36  factory_meta: Option<FactoryMeta>,
37  build_info: BuildInfo,
38  build_meta: BuildMeta,
39}
40
41impl ConsumeSharedModule {
42  pub fn new(context: Context, options: ConsumeOptions) -> Self {
43    let identifier = format!(
44      "consume shared module ({}) {}@{}{}{}{}{}",
45      &options.share_scope,
46      &options.share_key,
47      options
48        .required_version
49        .as_ref()
50        .map(|v| v.to_string())
51        .unwrap_or_else(|| "*".to_string()),
52      if options.strict_version {
53        " (strict)"
54      } else {
55        Default::default()
56      },
57      if options.singleton {
58        " (strict)"
59      } else {
60        Default::default()
61      },
62      options
63        .import_resolved
64        .as_ref()
65        .map(|f| format!(" (fallback: {f})"))
66        .unwrap_or_default(),
67      if options.eager {
68        " (eager)"
69      } else {
70        Default::default()
71      },
72    );
73    Self {
74      blocks: Vec::new(),
75      dependencies: Vec::new(),
76      identifier: ModuleIdentifier::from(identifier.as_ref()),
77      lib_ident: format!(
78        "webpack/sharing/consume/{}/{}{}",
79        &options.share_scope,
80        &options.share_key,
81        options
82          .import
83          .as_ref()
84          .map(|r| format!("/{r}"))
85          .unwrap_or_default()
86      ),
87      readable_identifier: identifier,
88      context,
89      options,
90      factory_meta: None,
91      build_info: Default::default(),
92      build_meta: Default::default(),
93      source_map_kind: SourceMapKind::empty(),
94    }
95  }
96}
97
98impl Identifiable for ConsumeSharedModule {
99  fn identifier(&self) -> Identifier {
100    self.identifier
101  }
102}
103
104impl DependenciesBlock for ConsumeSharedModule {
105  fn add_block_id(&mut self, block: AsyncDependenciesBlockIdentifier) {
106    self.blocks.push(block)
107  }
108
109  fn get_blocks(&self) -> &[AsyncDependenciesBlockIdentifier] {
110    &self.blocks
111  }
112
113  fn add_dependency_id(&mut self, dependency: DependencyId) {
114    self.dependencies.push(dependency)
115  }
116
117  fn remove_dependency_id(&mut self, dependency: DependencyId) {
118    self.dependencies.retain(|d| d != &dependency)
119  }
120
121  fn get_dependencies(&self) -> &[DependencyId] {
122    &self.dependencies
123  }
124}
125
126#[cacheable_dyn]
127#[async_trait]
128impl Module for ConsumeSharedModule {
129  impl_module_meta_info!();
130
131  fn size(&self, _source_type: Option<&SourceType>, _compilation: Option<&Compilation>) -> f64 {
132    42.0
133  }
134
135  fn module_type(&self) -> &ModuleType {
136    &ModuleType::ConsumeShared
137  }
138
139  fn source_types(&self, _module_graph: &ModuleGraph) -> &[SourceType] {
140    &[SourceType::ConsumeShared]
141  }
142
143  fn source(&self) -> Option<&BoxSource> {
144    None
145  }
146
147  fn readable_identifier(&self, _context: &Context) -> Cow<'_, str> {
148    self.readable_identifier.as_str().into()
149  }
150
151  fn lib_ident(&self, _options: LibIdentOptions) -> Option<Cow<'_, str>> {
152    Some(self.lib_ident.as_str().into())
153  }
154
155  fn get_context(&self) -> Option<Box<Context>> {
156    Some(Box::new(self.context.clone()))
157  }
158
159  async fn build(
160    &mut self,
161    _build_context: BuildContext,
162    _: Option<&Compilation>,
163  ) -> Result<BuildResult> {
164    let mut blocks = vec![];
165    let mut dependencies = vec![];
166    if let Some(fallback) = &self.options.import {
167      let dep = Box::new(ConsumeSharedFallbackDependency::new(fallback.to_owned()));
168      if self.options.eager {
169        dependencies.push(dep as BoxDependency);
170      } else {
171        let block = AsyncDependenciesBlock::new(self.identifier, None, None, vec![dep], None);
172        blocks.push(Box::new(block));
173      }
174    }
175
176    Ok(BuildResult {
177      dependencies,
178      blocks,
179      ..Default::default()
180    })
181  }
182
183  // #[tracing::instrument("ConsumeSharedModule::code_generation", skip_all, fields(identifier = ?self.identifier()))]
184  async fn code_generation(
185    &self,
186    compilation: &Compilation,
187    _runtime: Option<&RuntimeSpec>,
188    _: Option<ConcatenationScope>,
189  ) -> Result<CodeGenerationResult> {
190    let mut code_generation_result = CodeGenerationResult::default();
191    code_generation_result
192      .runtime_requirements
193      .insert(RuntimeGlobals::SHARE_SCOPE_MAP);
194    let mut function = String::from("loaders.load");
195    let mut args = vec![
196      json_stringify(&self.options.share_scope),
197      json_stringify(&self.options.share_key),
198    ];
199    if let Some(version) = &self.options.required_version {
200      if self.options.strict_version {
201        function += "Strict";
202      }
203      if self.options.singleton {
204        function += "Singleton";
205      }
206      let version = json_stringify(&version.to_string());
207      args.push(format!("loaders.parseRange({version})"));
208      function += "VersionCheck";
209    } else if self.options.singleton {
210      function += "Singleton";
211    }
212    let factory = self.options.import.as_ref().map(|fallback| {
213      if self.options.eager {
214        sync_module_factory(
215          &self.get_dependencies()[0],
216          fallback,
217          compilation,
218          &mut code_generation_result.runtime_requirements,
219        )
220      } else {
221        async_module_factory(
222          &self.get_blocks()[0],
223          fallback,
224          compilation,
225          &mut code_generation_result.runtime_requirements,
226        )
227      }
228    });
229    code_generation_result
230      .data
231      .insert(CodeGenerationDataConsumeShared {
232        share_scope: self.options.share_scope.clone(),
233        share_key: self.options.share_key.clone(),
234        import: self.options.import.clone(),
235        required_version: self.options.required_version.clone(),
236        strict_version: self.options.strict_version,
237        singleton: self.options.singleton,
238        eager: self.options.eager,
239        fallback: factory,
240      });
241    Ok(code_generation_result)
242  }
243
244  async fn get_runtime_hash(
245    &self,
246    compilation: &Compilation,
247    runtime: Option<&RuntimeSpec>,
248  ) -> Result<RspackHashDigest> {
249    let mut hasher = RspackHash::from(&compilation.options.output);
250    self.options.dyn_hash(&mut hasher);
251    module_update_hash(self, &mut hasher, compilation, runtime);
252    Ok(hasher.digest(&compilation.options.output.hash_digest))
253  }
254}
255
256impl_empty_diagnosable_trait!(ConsumeSharedModule);