rspack_plugin_mf/sharing/
share_runtime_module.rs1use hashlink::{LinkedHashMap, LinkedHashSet};
2use itertools::Itertools;
3use rspack_collections::Identifier;
4use rspack_core::{
5 ChunkUkey, Compilation, ModuleId, RuntimeGlobals, RuntimeModule, SourceType, impl_runtime_module,
6};
7use rustc_hash::FxHashMap;
8
9use super::provide_shared_plugin::ProvideVersion;
10use crate::{ConsumeVersion, utils::json_stringify};
11
12#[impl_runtime_module]
13#[derive(Debug)]
14pub struct ShareRuntimeModule {
15 id: Identifier,
16 chunk: Option<ChunkUkey>,
17 enhanced: bool,
18}
19
20impl ShareRuntimeModule {
21 pub fn new(enhanced: bool) -> Self {
22 Self::with_default(Identifier::from("webpack/runtime/sharing"), None, enhanced)
23 }
24}
25
26#[async_trait::async_trait]
27impl RuntimeModule for ShareRuntimeModule {
28 fn name(&self) -> Identifier {
29 self.id
30 }
31
32 async fn generate(&self, compilation: &Compilation) -> rspack_error::Result<String> {
33 let chunk_ukey = self
34 .chunk
35 .expect("should have chunk in <ShareRuntimeModule as RuntimeModule>::generate");
36 let chunk = compilation.chunk_by_ukey.expect_get(&chunk_ukey);
37 let module_graph = compilation.get_module_graph();
38 let mut init_per_scope: FxHashMap<
39 String,
40 LinkedHashMap<DataInitStage, LinkedHashSet<DataInitInfo>>,
41 > = FxHashMap::default();
42 for c in chunk.get_all_referenced_chunks(&compilation.chunk_group_by_ukey) {
43 let chunk = compilation.chunk_by_ukey.expect_get(&c);
44 let mut modules = compilation
45 .chunk_graph
46 .get_chunk_modules_identifier_by_source_type(&c, SourceType::ShareInit, &module_graph);
47 modules.sort_unstable();
48 for mid in modules {
49 let code_gen = compilation
50 .code_generation_results
51 .get(&mid, Some(chunk.runtime()));
52 let Some(data) = code_gen.data.get::<CodeGenerationDataShareInit>() else {
53 continue;
54 };
55 for item in &data.items {
56 let stages = init_per_scope.entry(item.share_scope.clone()).or_default();
57 let list = stages
58 .entry(item.init_stage)
59 .or_insert_with(LinkedHashSet::default);
60 list.insert(item.init.clone());
61 }
62 }
63 }
64 let scope_to_data_init = init_per_scope
65 .into_iter()
66 .sorted_unstable_by_key(|(scope, _)| scope.to_string())
67 .map(|(scope, stages)| {
68 let stages: Vec<String> = stages
69 .into_iter()
70 .sorted_unstable_by_key(|(stage, _)| *stage)
71 .flat_map(|(_, inits)| inits)
72 .map(|info| match info {
73 DataInitInfo::ExternalModuleId(Some(id)) => json_stringify(&id),
74 DataInitInfo::ProvideSharedInfo(info) => {
75 let mut stage = format!(
76 "{{ name: {}, version: {}, factory: {}, eager: {}",
77 json_stringify(&info.name),
78 json_stringify(&info.version.to_string()),
79 info.factory,
80 if info.eager { "1" } else { "0" },
81 );
82 if self.enhanced {
83 if let Some(singleton) = info.singleton {
84 stage += ", singleton: ";
85 stage += if singleton { "1" } else { "0" };
86 }
87 if let Some(required_version) = info.required_version {
88 stage += ", requiredVersion: ";
89 stage += &json_stringify(&required_version.to_string());
90 }
91 if let Some(strict_version) = info.strict_version {
92 stage += ", strictVersion: ";
93 stage += if strict_version { "1" } else { "0" };
94 }
95 }
96 stage += " }";
97 stage
98 }
99 _ => "".to_string(),
100 })
101 .collect();
102 format!("{}: [{}]", json_stringify(&scope), stages.join(", "))
103 })
104 .collect::<Vec<_>>()
105 .join(", ");
106 let initialize_sharing_impl = if self.enhanced {
107 "__webpack_require__.I = __webpack_require__.I || function() { throw new Error(\"should have __webpack_require__.I\") }"
108 } else {
109 include_str!("./initializeSharing.js")
110 };
111 Ok(format!(
112 r#"
113{share_scope_map} = {{}};
114__webpack_require__.initializeSharingData = {{ scopeToSharingDataMapping: {{ {scope_to_data_init} }}, uniqueName: {unique_name} }};
115{initialize_sharing_impl}
116"#,
117 share_scope_map = RuntimeGlobals::SHARE_SCOPE_MAP,
118 scope_to_data_init = scope_to_data_init,
119 unique_name = json_stringify(&compilation.options.output.unique_name),
120 initialize_sharing_impl = initialize_sharing_impl,
121 ))
122 }
123
124 fn attach(&mut self, chunk: ChunkUkey) {
125 self.chunk = Some(chunk);
126 }
127}
128
129#[derive(Debug, Clone)]
130pub struct CodeGenerationDataShareInit {
131 pub items: Vec<ShareInitData>,
132}
133
134#[derive(Debug, Clone)]
135pub struct ShareInitData {
136 pub share_scope: String,
137 pub init_stage: DataInitStage,
138 pub init: DataInitInfo,
139}
140
141pub type DataInitStage = i8;
142
143#[derive(Debug, Clone, PartialEq, Eq, Hash)]
144pub enum DataInitInfo {
145 ExternalModuleId(Option<ModuleId>),
146 ProvideSharedInfo(ProvideSharedInfo),
147}
148
149#[derive(Debug, Clone, PartialEq, Eq, Hash)]
150pub struct ProvideSharedInfo {
151 pub name: String,
152 pub version: ProvideVersion,
153 pub factory: String,
154 pub eager: bool,
155 pub singleton: Option<bool>,
156 pub required_version: Option<ConsumeVersion>,
157 pub strict_version: Option<bool>,
158}