rspack_plugin_runtime/
common_js_chunk_format.rs1use std::hash::Hash;
2
3use rspack_core::{
4 ChunkUkey, Compilation, CompilationAdditionalChunkRuntimeRequirements,
5 CompilationDependentFullHash, CompilationParams, CompilerCompilation, Plugin, RuntimeGlobals,
6 rspack_sources::{ConcatSource, RawStringSource, SourceExt},
7};
8use rspack_error::Result;
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,
14};
15use rspack_util::json_stringify;
16
17use crate::{
18 generate_entry_startup, get_chunk_output_name, get_relative_path, get_runtime_chunk_output_name,
19 runtime_chunk_has_hash, update_hash_for_entry_startup,
20};
21
22const PLUGIN_NAME: &str = "rspack.CommonJsChunkFormatPlugin";
23
24#[plugin]
25#[derive(Debug, Default)]
26pub struct CommonJsChunkFormatPlugin;
27
28#[plugin_hook(CompilerCompilation for CommonJsChunkFormatPlugin)]
29async fn compilation(
30 &self,
31 compilation: &mut Compilation,
32 _params: &mut CompilationParams,
33) -> Result<()> {
34 let hooks = JsPlugin::get_compilation_hooks_mut(compilation.id());
35 let mut hooks = hooks.write().await;
36 hooks.chunk_hash.tap(js_chunk_hash::new(self));
37 hooks.render_chunk.tap(render_chunk::new(self));
38 Ok(())
39}
40
41#[plugin_hook(CompilationAdditionalChunkRuntimeRequirements for CommonJsChunkFormatPlugin)]
42async fn additional_chunk_runtime_requirements(
43 &self,
44 compilation: &mut Compilation,
45 chunk_ukey: &ChunkUkey,
46 runtime_requirements: &mut RuntimeGlobals,
47) -> Result<()> {
48 let chunk = compilation.chunk_by_ukey.expect_get(chunk_ukey);
49
50 if chunk.has_runtime(&compilation.chunk_group_by_ukey) {
51 return Ok(());
52 }
53
54 if compilation
55 .chunk_graph
56 .get_number_of_entry_modules(chunk_ukey)
57 > 0
58 {
59 runtime_requirements.insert(RuntimeGlobals::REQUIRE);
60 runtime_requirements.insert(RuntimeGlobals::STARTUP_ENTRYPOINT);
61 runtime_requirements.insert(RuntimeGlobals::EXTERNAL_INSTALL_CHUNK);
62 }
63
64 Ok(())
65}
66
67#[plugin_hook(JavascriptModulesChunkHash for CommonJsChunkFormatPlugin)]
68async fn js_chunk_hash(
69 &self,
70 compilation: &Compilation,
71 chunk_ukey: &ChunkUkey,
72 hasher: &mut RspackHash,
73) -> Result<()> {
74 let chunk = compilation.chunk_by_ukey.expect_get(chunk_ukey);
75 if chunk.has_runtime(&compilation.chunk_group_by_ukey) {
76 return Ok(());
77 }
78
79 PLUGIN_NAME.hash(hasher);
80
81 update_hash_for_entry_startup(
82 hasher,
83 compilation,
84 compilation
85 .chunk_graph
86 .get_chunk_entry_modules_with_chunk_group_iterable(chunk_ukey),
87 chunk_ukey,
88 );
89
90 Ok(())
91}
92
93#[plugin_hook(CompilationDependentFullHash for CommonJsChunkFormatPlugin)]
94async fn compilation_dependent_full_hash(
95 &self,
96 compilation: &Compilation,
97 chunk_ukey: &ChunkUkey,
98) -> Result<Option<bool>> {
99 let chunk = compilation.chunk_by_ukey.expect_get(chunk_ukey);
100 if chunk.has_entry_module(&compilation.chunk_graph)
101 && runtime_chunk_has_hash(compilation, chunk_ukey).await?
102 {
103 return Ok(Some(true));
104 }
105 Ok(None)
106}
107
108#[plugin_hook(JavascriptModulesRenderChunk for CommonJsChunkFormatPlugin)]
109async fn render_chunk(
110 &self,
111 compilation: &Compilation,
112 chunk_ukey: &ChunkUkey,
113 render_source: &mut RenderSource,
114) -> Result<()> {
115 let hooks = JsPlugin::get_compilation_hooks(compilation.id());
116 let chunk = compilation.chunk_by_ukey.expect_get(chunk_ukey);
117 let base_chunk_output_name = get_chunk_output_name(chunk, compilation).await?;
118 let mut sources = ConcatSource::default();
119 sources.add(RawStringSource::from(format!(
120 "exports.ids = [{}];\n",
121 json_stringify(chunk.expect_id(&compilation.chunk_ids_artifact))
122 )));
123 sources.add(RawStringSource::from_static("exports.modules = "));
124 sources.add(render_source.source.clone());
125 sources.add(RawStringSource::from_static(";\n"));
126 if compilation
127 .chunk_graph
128 .has_chunk_runtime_modules(chunk_ukey)
129 {
130 sources.add(RawStringSource::from_static("exports.runtime = "));
131 sources.add(render_chunk_runtime_modules(compilation, chunk_ukey).await?);
132 sources.add(RawStringSource::from_static(";\n"));
133 }
134
135 if chunk.has_entry_module(&compilation.chunk_graph) {
136 let runtime_chunk_output_name = get_runtime_chunk_output_name(compilation, chunk_ukey).await?;
137 sources.add(RawStringSource::from(format!(
138 "// load runtime\nvar {} = require({});\n",
139 RuntimeGlobals::REQUIRE,
140 json_stringify(&get_relative_path(
141 base_chunk_output_name
142 .trim_start_matches("/")
143 .trim_start_matches("\\"),
144 &runtime_chunk_output_name
145 ))
146 )));
147 sources.add(RawStringSource::from(format!(
148 "{}(exports)\n",
149 RuntimeGlobals::EXTERNAL_INSTALL_CHUNK,
150 )));
151
152 let entries = compilation
153 .chunk_graph
154 .get_chunk_entry_modules_with_chunk_group_iterable(chunk_ukey);
155 let start_up_source = generate_entry_startup(compilation, chunk_ukey, entries, false);
156 let last_entry_module = entries
157 .keys()
158 .next_back()
159 .expect("should have last entry module");
160 let mut startup_render_source = RenderSource {
161 source: start_up_source,
162 };
163 hooks
164 .try_read()
165 .expect("should have js plugin drive")
166 .render_startup
167 .call(
168 compilation,
169 chunk_ukey,
170 last_entry_module,
171 &mut startup_render_source,
172 )
173 .await?;
174 sources.add(startup_render_source.source);
175 render_source.source = ConcatSource::new([
176 RawStringSource::from_static("(function() {\n").boxed(),
177 sources.boxed(),
178 RawStringSource::from_static("\n})()").boxed(),
179 ])
180 .boxed();
181 return Ok(());
182 }
183 render_source.source = sources.boxed();
184 Ok(())
185}
186
187impl Plugin for CommonJsChunkFormatPlugin {
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 ctx
199 .compilation_hooks
200 .dependent_full_hash
201 .tap(compilation_dependent_full_hash::new(self));
202 Ok(())
203 }
204}