rspack_plugin_mf/sharing/
share_runtime_module.rs

1use 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}