rspack_plugin_mf/sharing/
consume_shared_module.rs1use 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 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);