rspack_plugin_runtime/
array_push_callback_chunk_format.rs

1use std::hash::Hash;
2
3use rspack_core::{
4  ChunkGraph, ChunkKind, ChunkUkey, Compilation, CompilationAdditionalChunkRuntimeRequirements,
5  CompilationParams, CompilerCompilation, Plugin, RuntimeGlobals,
6  rspack_sources::{ConcatSource, RawStringSource, SourceExt},
7};
8use rspack_error::{Result, ToStringResultToRspackResultExt};
9use rspack_hash::RspackHash;
10use rspack_hook::{plugin, plugin_hook};
11use rspack_plugin_javascript::{
12  JavascriptModulesChunkHash, JavascriptModulesRenderChunk, JsPlugin, RenderSource,
13  runtime::{render_chunk_runtime_modules, render_runtime_modules},
14};
15use rspack_util::json_stringify;
16
17use super::{generate_entry_startup, update_hash_for_entry_startup};
18
19const PLUGIN_NAME: &str = "rspack.ArrayPushCallbackChunkFormatPlugin";
20
21#[plugin]
22#[derive(Debug, Default)]
23pub struct ArrayPushCallbackChunkFormatPlugin;
24
25#[plugin_hook(CompilerCompilation for ArrayPushCallbackChunkFormatPlugin)]
26async fn compilation(
27  &self,
28  compilation: &mut Compilation,
29  _params: &mut CompilationParams,
30) -> Result<()> {
31  let hooks = JsPlugin::get_compilation_hooks_mut(compilation.id());
32  let mut hooks = hooks.write().await;
33  hooks.chunk_hash.tap(js_chunk_hash::new(self));
34  hooks.render_chunk.tap(render_chunk::new(self));
35  Ok(())
36}
37
38#[plugin_hook(CompilationAdditionalChunkRuntimeRequirements for ArrayPushCallbackChunkFormatPlugin)]
39async fn additional_chunk_runtime_requirements(
40  &self,
41  compilation: &mut Compilation,
42  chunk_ukey: &ChunkUkey,
43  runtime_requirements: &mut RuntimeGlobals,
44) -> Result<()> {
45  let chunk = compilation.chunk_by_ukey.expect_get(chunk_ukey);
46
47  if chunk.has_runtime(&compilation.chunk_group_by_ukey) {
48    return Ok(());
49  }
50
51  if compilation
52    .chunk_graph
53    .get_number_of_entry_modules(chunk_ukey)
54    > 0
55  {
56    runtime_requirements.insert(RuntimeGlobals::ON_CHUNKS_LOADED);
57    runtime_requirements.insert(RuntimeGlobals::EXPORTS);
58    runtime_requirements.insert(RuntimeGlobals::REQUIRE);
59  }
60  runtime_requirements.insert(RuntimeGlobals::CHUNK_CALLBACK);
61
62  Ok(())
63}
64
65#[plugin_hook(JavascriptModulesChunkHash for ArrayPushCallbackChunkFormatPlugin)]
66async fn js_chunk_hash(
67  &self,
68  compilation: &Compilation,
69  chunk_ukey: &ChunkUkey,
70  hasher: &mut RspackHash,
71) -> Result<()> {
72  let chunk = compilation.chunk_by_ukey.expect_get(chunk_ukey);
73  if chunk.has_runtime(&compilation.chunk_group_by_ukey) {
74    return Ok(());
75  }
76
77  PLUGIN_NAME.hash(hasher);
78  let output = &compilation.options.output;
79  output.global_object.hash(hasher);
80  output.chunk_loading_global.hash(hasher);
81  output.hot_update_global.hash(hasher);
82
83  update_hash_for_entry_startup(
84    hasher,
85    compilation,
86    compilation
87      .chunk_graph
88      .get_chunk_entry_modules_with_chunk_group_iterable(chunk_ukey),
89    chunk_ukey,
90  );
91
92  Ok(())
93}
94
95#[plugin_hook(JavascriptModulesRenderChunk for ArrayPushCallbackChunkFormatPlugin)]
96async fn render_chunk(
97  &self,
98  compilation: &Compilation,
99  chunk_ukey: &ChunkUkey,
100  render_source: &mut RenderSource,
101) -> Result<()> {
102  let hooks = JsPlugin::get_compilation_hooks(compilation.id());
103  let chunk = compilation.chunk_by_ukey.expect_get(chunk_ukey);
104  let has_runtime_modules = compilation
105    .chunk_graph
106    .has_chunk_runtime_modules(chunk_ukey);
107  let global_object = &compilation.options.output.global_object;
108  let hot_update_global = &compilation.options.output.hot_update_global;
109  let mut source = ConcatSource::default();
110
111  if matches!(chunk.kind(), ChunkKind::HotUpdate) {
112    source.add(RawStringSource::from(format!(
113      "{}[{}]({}, ",
114      global_object,
115      serde_json::to_string(hot_update_global).to_rspack_result()?,
116      json_stringify(chunk.expect_id(&compilation.chunk_ids_artifact))
117    )));
118    source.add(render_source.source.clone());
119    if has_runtime_modules {
120      source.add(RawStringSource::from_static(","));
121      source.add(render_chunk_runtime_modules(compilation, chunk_ukey).await?);
122    }
123    source.add(RawStringSource::from_static(")"));
124  } else {
125    let chunk_loading_global = &compilation.options.output.chunk_loading_global;
126
127    source.add(RawStringSource::from(format!(
128      r#"({}["{}"] = {}["{}"] || []).push([[{}], "#,
129      global_object,
130      chunk_loading_global,
131      global_object,
132      chunk_loading_global,
133      serde_json::to_string(chunk.expect_id(&compilation.chunk_ids_artifact))
134        .expect("json stringify failed"),
135    )));
136    source.add(render_source.source.clone());
137    let has_entry = chunk.has_entry_module(&compilation.chunk_graph);
138    if has_entry || has_runtime_modules {
139      source.add(RawStringSource::from_static(","));
140      source.add(RawStringSource::from(format!(
141        "function({}) {{\n",
142        RuntimeGlobals::REQUIRE
143      )));
144      if has_runtime_modules {
145        source.add(render_runtime_modules(compilation, chunk_ukey).await?);
146      }
147      if has_entry {
148        let entries = compilation
149          .chunk_graph
150          .get_chunk_entry_modules_with_chunk_group_iterable(chunk_ukey);
151        let start_up_source = generate_entry_startup(compilation, chunk_ukey, entries, true);
152        let last_entry_module = entries
153          .keys()
154          .next_back()
155          .expect("should have last entry module");
156        let mut render_source = RenderSource {
157          source: start_up_source,
158        };
159        hooks
160          .try_read()
161          .expect("should have js plugin drive")
162          .render_startup
163          .call(
164            compilation,
165            chunk_ukey,
166            last_entry_module,
167            &mut render_source,
168          )
169          .await?;
170        source.add(render_source.source);
171        let runtime_requirements =
172          ChunkGraph::get_tree_runtime_requirements(compilation, chunk_ukey);
173        if runtime_requirements.contains(RuntimeGlobals::RETURN_EXPORTS_FROM_RUNTIME) {
174          source.add(RawStringSource::from_static(
175            "return __webpack_exports__;\n",
176          ));
177        }
178      }
179      source.add(RawStringSource::from_static("\n}\n"));
180    }
181    source.add(RawStringSource::from_static("])"));
182  }
183  render_source.source = source.boxed();
184  Ok(())
185}
186
187impl Plugin for ArrayPushCallbackChunkFormatPlugin {
188  fn name(&self) -> &'static str {
189    PLUGIN_NAME
190  }
191
192  fn apply(&self, ctx: &mut rspack_core::ApplyContext<'_>) -> Result<()> {
193    ctx.compiler_hooks.compilation.tap(compilation::new(self));
194    ctx
195      .compilation_hooks
196      .additional_chunk_runtime_requirements
197      .tap(additional_chunk_runtime_requirements::new(self));
198    Ok(())
199  }
200}