rspack_plugin_runtime/
array_push_callback_chunk_format.rs1use 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}