1use std::borrow::Cow;
2
3use rspack_core::{
4 ChunkCodeTemplate, ChunkGraph, ChunkInitFragments, ChunkRenderContext, ChunkUkey,
5 CodeGenerationDataTopLevelDeclarations, Compilation, ExportsArgument, Module, RuntimeGlobals,
6 RuntimeVariable, SourceType, property_access, render_init_fragments,
7 rspack_sources::{BoxSource, ConcatSource, RawStringSource, SourceExt},
8};
9use rspack_error::Result;
10
11use super::{JsPlugin, RenderBootstrapResult};
12use crate::{
13 RenderSource,
14 runtime::{
15 render_chunk_modules, render_module, render_runtime_context_declaration,
16 render_runtime_context_require_assignment, render_runtime_modules, stringify_array,
17 },
18};
19
20impl JsPlugin {
21 pub fn render_rspack_require<'me>(
22 chunk_ukey: &ChunkUkey,
23 compilation: &'me Compilation,
24 runtime_template: &ChunkCodeTemplate,
25 ) -> Vec<Cow<'me, str>> {
26 let runtime_requirements = compilation
27 .cgc_runtime_requirements_artifact
28 .get(chunk_ukey)
29 .copied()
30 .unwrap_or_default();
31
32 let strict_module_error_handling = compilation.options.output.strict_module_error_handling;
33 let need_module_defer =
34 runtime_requirements.contains(RuntimeGlobals::MAKE_DEFERRED_NAMESPACE_OBJECT);
35 let callable_require = runtime_template.render_runtime_variable(&RuntimeVariable::Require);
36 let require_argument = runtime_template.render_runtime_argument();
37 let runtime_context = runtime_template.render_runtime_variable(&RuntimeVariable::Context);
38 let module_factories = runtime_template.render_runtime_variable(&RuntimeVariable::Modules);
39 let module_cache = runtime_template.render_runtime_variable(&RuntimeVariable::ModuleCache);
40 let mut sources: Vec<Cow<str>> = Vec::new();
41
42 sources.push(
43 format!(
44 r#"// Check if module is in cache
45var cachedModule = {module_cache}[moduleId];
46if (cachedModule !== undefined) {{"#,
47 )
48 .into(),
49 );
50
51 if strict_module_error_handling {
52 sources.push("if (cachedModule.error !== undefined) throw cachedModule.error;".into());
53 }
54
55 sources.push(
56 format!(
57 r#"return cachedModule.exports;
58}}
59// Create a new module (and put it into the cache)
60var module = ({module_cache}[moduleId] = {{"#,
61 )
62 .into(),
63 );
64
65 if runtime_requirements.contains(RuntimeGlobals::MODULE_ID) {
66 sources.push("id: moduleId,".into());
67 }
68
69 if runtime_requirements.contains(RuntimeGlobals::MODULE_LOADED) {
70 sources.push("loaded: false,".into());
71 }
72
73 if need_module_defer {
74 sources.push("exports: __rspack_deferred_exports[moduleId] || {}".into());
75 } else {
76 sources.push("exports: {}".into());
77 }
78 sources.push("});\n// Execute the module function".into());
79
80 let module_execution = if runtime_requirements
81 .contains(RuntimeGlobals::INTERCEPT_MODULE_EXECUTION)
82 {
83 format!(
84 r#"
85 var execOptions = {{ id: moduleId, module: module, factory: {module_factories}[moduleId], require: {callable_require}, context: Object.create({runtime_context}) }};
86 {}.forEach(function(handler) {{ handler(execOptions); }});
87 module = execOptions.module;
88 execOptions.factory.call(module.exports, module, module.exports, execOptions.context);
89 "#,
90 runtime_template.render_runtime_globals(&RuntimeGlobals::INTERCEPT_MODULE_EXECUTION)
91 )
92 .into()
93 } else if runtime_requirements.contains(RuntimeGlobals::THIS_AS_EXPORTS) {
94 format!(
95 "{module_factories}[moduleId].call(module.exports, module, module.exports, {require_argument});\n"
96 )
97 .into()
98 } else {
99 format!("{module_factories}[moduleId](module, module.exports, {require_argument});\n").into()
100 };
101
102 if strict_module_error_handling {
103 sources.push("try {\n".into());
104 sources.push(module_execution);
105 sources.push("} catch (e) {".into());
106 if need_module_defer {
107 sources.push("delete __rspack_deferred_exports[moduleId];".into());
108 }
109 sources.push("module.error = e;\nthrow e;".into());
110 sources.push("}".into());
111 } else {
112 sources.push(module_execution);
113 if need_module_defer {
114 sources.push("delete __rspack_deferred_exports[moduleId];".into());
115 }
116 }
117
118 if runtime_requirements.contains(RuntimeGlobals::MODULE_LOADED) {
119 sources.push("// Flag the module as loaded\nmodule.loaded = true;".into());
120 }
121
122 sources.push("// Return the exports of the module\nreturn module.exports;".into());
123
124 sources
125 }
126
127 pub async fn render_rspack_bootstrap<'me>(
128 chunk_ukey: &ChunkUkey,
129 compilation: &'me Compilation,
130 runtime_template: &ChunkCodeTemplate,
131 ) -> Result<RenderBootstrapResult<'me>> {
132 let runtime_requirements = compilation
133 .cgc_runtime_requirements_artifact
134 .get(chunk_ukey)
135 .copied()
136 .unwrap_or_default();
137 let chunk = compilation
138 .build_chunk_graph_artifact
139 .chunk_by_ukey
140 .expect_get(chunk_ukey);
141 let module_factories = runtime_requirements.contains(RuntimeGlobals::MODULE_FACTORIES);
142 let require_function = runtime_requirements.contains(RuntimeGlobals::REQUIRE);
143 let module_cache = runtime_requirements.contains(RuntimeGlobals::MODULE_CACHE);
144 let intercept_module_execution =
145 runtime_requirements.contains(RuntimeGlobals::INTERCEPT_MODULE_EXECUTION);
146 let module_used = runtime_requirements.contains(RuntimeGlobals::MODULE);
147 let has_custom_runtime_module = compilation
148 .build_chunk_graph_artifact
149 .chunk_graph
150 .get_chunk_runtime_modules_iterable(chunk_ukey)
151 .any(|runtime_module_identifier| {
152 let runtime_module = &compilation.runtime_modules[runtime_module_identifier];
153 runtime_module.get_custom_source().is_some()
154 || runtime_module.get_constructor_name() == "RuntimeModuleFromJs"
155 });
156 let require_scope_used = runtime_requirements.contains(RuntimeGlobals::REQUIRE_SCOPE)
157 || !runtime_requirements.renderable_require_scope().is_empty()
158 || has_custom_runtime_module;
159 let need_module_defer =
160 runtime_requirements.contains(RuntimeGlobals::MAKE_DEFERRED_NAMESPACE_OBJECT);
161 let use_require = require_function || intercept_module_execution || module_used;
162 let mut header: Vec<Cow<str>> = Vec::new();
163 let mut startup: Vec<Cow<str>> = Vec::new();
164 let mut allow_inline_startup = true;
165 let supports_arrow_function = compilation
166 .options
167 .output
168 .environment
169 .supports_arrow_function();
170 let has_bootstrap_runtime_context = runtime_requirements.needs_bootstrap_runtime_context();
171
172 if allow_inline_startup && module_factories {
173 startup.push("// module factories are used so entry inlining is disabled".into());
174 allow_inline_startup = false;
175 }
176 if allow_inline_startup && module_cache {
177 startup.push("// module cache are used so entry inlining is disabled".into());
178 allow_inline_startup = false;
179 }
180 if allow_inline_startup && intercept_module_execution {
181 startup.push("// module execution is intercepted so entry inlining is disabled".into());
182 allow_inline_startup = false;
183 }
184
185 if use_require || module_cache {
186 header.push(
187 format!(
188 r#"// The module cache
189var {} = {{}};
190"#,
191 runtime_template.render_runtime_variable(&RuntimeVariable::ModuleCache)
192 )
193 .into(),
194 );
195 }
196
197 if need_module_defer {
198 header.push(
199 r#"// The deferred module cache
200var __rspack_deferred_exports = {};
201"#
202 .into(),
203 );
204 }
205
206 if has_bootstrap_runtime_context {
207 header.push(render_runtime_context_declaration(runtime_template).into());
208 }
209
210 if use_require {
211 header.push(
212 format!(
213 r#"// The require function
214function {}(moduleId) {{
215"#,
216 runtime_template.render_runtime_variable(&RuntimeVariable::Require)
217 )
218 .into(),
219 );
220 header.extend(Self::render_rspack_require(
221 chunk_ukey,
222 compilation,
223 runtime_template,
224 ));
225 header.push(
226 r#"
227}
228"#
229 .into(),
230 );
231 header.push(render_runtime_context_require_assignment(runtime_template).into());
232 } else if require_scope_used && !has_bootstrap_runtime_context {
233 header.push(render_runtime_context_declaration(runtime_template).into());
234 }
235
236 if module_factories || runtime_requirements.contains(RuntimeGlobals::MODULE_FACTORIES_ADD_ONLY)
237 {
238 let runtime_context = runtime_template.render_runtime_variable(&RuntimeVariable::Context);
239 let name = RuntimeGlobals::MODULE_FACTORIES
240 .rspack_context_property_name()
241 .expect("module factories should have context property name");
242 let module_factories = format!("{runtime_context}{}", property_access([name], 0));
243 header.push(
244 format!(
245 r#"// expose the modules object ({modules})
246{module_factories} = {modules};
247"#,
248 modules = runtime_template.render_runtime_variable(&RuntimeVariable::Modules),
249 module_factories = module_factories
250 )
251 .into(),
252 );
253 }
254
255 if runtime_requirements.contains(RuntimeGlobals::MODULE_CACHE) {
256 let runtime_context = runtime_template.render_runtime_variable(&RuntimeVariable::Context);
257 let name = RuntimeGlobals::MODULE_CACHE
258 .rspack_context_property_name()
259 .expect("module cache should have context property name");
260 let module_cache_runtime_global = format!("{runtime_context}{}", property_access([name], 0));
261 header.push(
262 format!(
263 r#"// expose the module cache
264{} = {};
265"#,
266 module_cache_runtime_global,
267 runtime_template.render_runtime_variable(&RuntimeVariable::ModuleCache),
268 )
269 .into(),
270 );
271 }
272
273 if intercept_module_execution {
274 let runtime_context = runtime_template.render_runtime_variable(&RuntimeVariable::Context);
275 let name = RuntimeGlobals::INTERCEPT_MODULE_EXECUTION
276 .rspack_context_property_name()
277 .expect("intercept module execution should have context property name");
278 let intercept_module_execution = format!("{runtime_context}{}", property_access([name], 0));
279 header.push(
280 format!(
281 r#"// expose the module execution interceptor
282{intercept_module_execution} = [];
283"#,
284 )
285 .into(),
286 );
287 }
288
289 if !runtime_requirements.contains(RuntimeGlobals::STARTUP_NO_DEFAULT) {
290 if chunk.has_entry_module(&compilation.build_chunk_graph_artifact.chunk_graph) {
291 let mut buf2: Vec<Cow<str>> = Vec::new();
292 buf2.push("// Load entry module and return exports".into());
293 let entries = compilation
294 .build_chunk_graph_artifact
295 .chunk_graph
296 .get_chunk_entry_modules_with_chunk_group_iterable(chunk_ukey);
297 let module_graph = compilation.get_module_graph();
298 for (i, (module, entry)) in entries.iter().enumerate() {
299 let chunk_group = compilation
300 .build_chunk_graph_artifact
301 .chunk_group_by_ukey
302 .expect_get(entry);
303 let chunk_ids = chunk_group
304 .chunks
305 .iter()
306 .filter(|c| *c != chunk_ukey)
307 .map(|chunk_ukey| {
308 compilation
309 .build_chunk_graph_artifact
310 .chunk_by_ukey
311 .expect_get(chunk_ukey)
312 .expect_id()
313 .to_string()
314 })
315 .collect::<Vec<_>>();
316 if allow_inline_startup && !chunk_ids.is_empty() {
317 buf2.push("// This entry module depends on other loaded chunks and execution need to be delayed".into());
318 allow_inline_startup = false;
319 }
320 if allow_inline_startup && {
321 let module_graph_cache = &compilation.module_graph_cache_artifact;
322 module_graph
323 .get_incoming_connections_by_origin_module(module)
324 .modules()
325 .iter()
326 .any(|(origin_module, connections)| {
327 connections.iter().any(|c| {
328 c.is_target_active(
329 module_graph,
330 Some(chunk.runtime()),
331 module_graph_cache,
332 &compilation
333 .build_module_graph_artifact
334 .side_effects_state_artifact,
335 &compilation.exports_info_artifact,
336 )
337 }) && compilation
338 .build_chunk_graph_artifact
339 .chunk_graph
340 .get_module_runtimes_iter(
341 *origin_module,
342 &compilation.build_chunk_graph_artifact.chunk_by_ukey,
343 )
344 .any(|runtime| runtime.intersection(chunk.runtime()).count() > 0)
345 })
346 } {
347 buf2.push(
348 "// This entry module is referenced by other modules so it can't be inlined".into(),
349 );
350 allow_inline_startup = false;
351 }
352 if allow_inline_startup && {
353 let codegen = compilation
354 .code_generation_results
355 .get(module, Some(chunk.runtime()));
356 let module_graph = compilation.get_module_graph();
357 let top_level_decls = codegen
358 .data
359 .get::<CodeGenerationDataTopLevelDeclarations>()
360 .map(|d| d.inner())
361 .or_else(|| {
362 module_graph
363 .module_by_identifier(module)
364 .and_then(|m| m.build_info().top_level_declarations.as_ref())
365 });
366 top_level_decls.is_none()
367 } {
368 buf2.push("// This entry module doesn't tell about it's top-level declarations so it can't be inlined".into());
369 allow_inline_startup = false;
370 }
371 let hooks = JsPlugin::get_compilation_hooks(compilation.id());
372 let bailout = hooks
373 .try_read()
374 .expect("should have js plugin drive")
375 .inline_in_runtime_bailout
376 .call(compilation)
377 .await?;
378 if allow_inline_startup && let Some(bailout) = bailout {
379 buf2.push(format!("// This entry module can't be inlined because {bailout}").into());
380 allow_inline_startup = false;
381 }
382 let entry_runtime_requirements =
383 ChunkGraph::get_module_runtime_requirements(compilation, *module, chunk.runtime());
384 if allow_inline_startup
385 && let Some(entry_runtime_requirements) = entry_runtime_requirements
386 && entry_runtime_requirements.contains(RuntimeGlobals::MODULE)
387 {
388 allow_inline_startup = false;
389 buf2.push("// This entry module used 'module' so it can't be inlined".into());
390 }
391
392 let module_id = ChunkGraph::get_module_id(&compilation.module_ids_artifact, *module)
393 .expect("should have module id");
394 let mut module_id_expr = rspack_util::json_stringify(module_id);
395 if runtime_requirements.contains(RuntimeGlobals::ENTRY_MODULE_ID) {
396 module_id_expr = format!(
397 "{} = {module_id_expr}",
398 runtime_template.render_runtime_globals(&RuntimeGlobals::ENTRY_MODULE_ID)
399 );
400 }
401
402 if !chunk_ids.is_empty() {
403 let on_chunks_loaded_callback = if supports_arrow_function {
404 format!(
405 "() => {}({module_id_expr})",
406 runtime_template.render_runtime_globals(&RuntimeGlobals::REQUIRE)
407 )
408 } else {
409 format!(
410 "function() {{ return {}({module_id_expr}) }}",
411 runtime_template.render_runtime_globals(&RuntimeGlobals::REQUIRE)
412 )
413 };
414 buf2.push(
415 format!(
416 "{}{}(undefined, {}, {});",
417 if i + 1 == entries.len() {
418 format!(
419 "var {} = ",
420 runtime_template.render_runtime_globals(&RuntimeGlobals::EXPORTS)
421 )
422 } else {
423 String::new()
424 },
425 runtime_template.render_runtime_globals(&RuntimeGlobals::ON_CHUNKS_LOADED),
426 stringify_array(&chunk_ids),
427 on_chunks_loaded_callback
428 )
429 .into(),
430 );
431 } else if use_require {
432 buf2.push(
433 format!(
434 "{}{}({module_id_expr});",
435 if i + 1 == entries.len() {
436 format!(
437 "var {} = ",
438 runtime_template.render_runtime_globals(&RuntimeGlobals::EXPORTS)
439 )
440 } else {
441 String::new()
442 },
443 runtime_template.render_runtime_globals(&RuntimeGlobals::REQUIRE)
444 )
445 .into(),
446 )
447 } else {
448 let should_exec = i + 1 == entries.len();
449 if should_exec {
450 buf2.push(
451 format!(
452 "var {} = {{}}",
453 runtime_template.render_runtime_globals(&RuntimeGlobals::EXPORTS)
454 )
455 .into(),
456 );
457 }
458 if require_scope_used {
459 buf2.push(
460 format!(
461 "{}[{module_id_expr}](0, {}, {});",
462 runtime_template.render_runtime_variable(&RuntimeVariable::Modules),
463 if should_exec {
464 runtime_template.render_runtime_globals(&RuntimeGlobals::EXPORTS)
465 } else {
466 "{}".to_string()
467 },
468 runtime_template.render_runtime_argument()
469 )
470 .into(),
471 );
472 } else if let Some(entry_runtime_requirements) = entry_runtime_requirements
473 && entry_runtime_requirements.contains(RuntimeGlobals::EXPORTS)
474 {
475 buf2.push(
476 format!(
477 "{}[{module_id_expr}](0, {});",
478 runtime_template.render_runtime_variable(&RuntimeVariable::Modules),
479 if should_exec {
480 runtime_template.render_runtime_globals(&RuntimeGlobals::EXPORTS)
481 } else {
482 "{}".to_string()
483 }
484 )
485 .into(),
486 );
487 } else {
488 buf2.push(
489 format!(
490 "{}[{module_id_expr}]();",
491 runtime_template.render_runtime_variable(&RuntimeVariable::Modules)
492 )
493 .into(),
494 );
495 }
496 }
497 }
498 if runtime_requirements.contains(RuntimeGlobals::ON_CHUNKS_LOADED) {
499 buf2.push(
500 format!(
501 "{exports} = {on_chunks_loaded}({exports});",
502 exports = runtime_template.render_runtime_variable(&RuntimeVariable::Exports),
503 on_chunks_loaded =
504 runtime_template.render_runtime_globals(&RuntimeGlobals::ON_CHUNKS_LOADED)
505 )
506 .into(),
507 );
508 }
509 if runtime_requirements.contains(RuntimeGlobals::STARTUP) {
510 let exports = runtime_template.render_runtime_globals(&RuntimeGlobals::EXPORTS);
511 allow_inline_startup = false;
512 header.push(
513 format!(
514 r#"// the startup function
515{} = {};
516"#,
517 runtime_template.render_runtime_globals(&RuntimeGlobals::STARTUP),
518 runtime_template
519 .basic_function("", &format!("{}\nreturn {}", buf2.join("\n"), exports))
520 )
521 .into(),
522 );
523 startup.push("// run startup".into());
524 startup.push(
525 format!(
526 "var {} = {}();",
527 runtime_template.render_runtime_globals(&RuntimeGlobals::EXPORTS),
528 runtime_template.render_runtime_globals(&RuntimeGlobals::STARTUP)
529 )
530 .into(),
531 );
532 } else {
533 startup.push("// startup".into());
534 startup.push(buf2.join("\n").into());
535 }
536 } else if runtime_requirements.contains(RuntimeGlobals::STARTUP) {
537 header.push(
538 format!(
539 r#"// the startup function
540// It's empty as no entry modules are in this chunk
541{} = function(){{}};"#,
542 runtime_template.render_runtime_globals(&RuntimeGlobals::STARTUP)
543 )
544 .into(),
545 );
546 }
547 } else if runtime_requirements.contains(RuntimeGlobals::STARTUP) {
548 header.push(
549 format!(
550 r#"// the startup function
551// It's empty as some runtime module handles the default behavior
552{} = function(){{}};"#,
553 runtime_template.render_runtime_globals(&RuntimeGlobals::STARTUP)
554 )
555 .into(),
556 );
557 startup.push("// run startup".into());
558 startup.push(
559 format!(
560 "var {} = {}();",
561 runtime_template.render_runtime_variable(&RuntimeVariable::Exports),
562 runtime_template.render_runtime_globals(&RuntimeGlobals::STARTUP)
563 )
564 .into(),
565 );
566 }
567
568 Ok(RenderBootstrapResult {
569 header,
570 startup,
571 allow_inline_startup,
572 })
573 }
574}
575
576impl JsPlugin {
577 pub async fn render_rspack_main(
578 &self,
579 compilation: &Compilation,
580 chunk_ukey: &ChunkUkey,
581 output_path: &str,
582 runtime_template: &ChunkCodeTemplate,
583 ) -> Result<BoxSource> {
584 let js_plugin_hooks = Self::get_compilation_hooks(compilation.id());
585 let hooks = js_plugin_hooks
586 .try_read()
587 .expect("should have js plugin drive");
588 let chunk = compilation
589 .build_chunk_graph_artifact
590 .chunk_by_ukey
591 .expect_get(chunk_ukey);
592 let supports_arrow_function = compilation
593 .options
594 .output
595 .environment
596 .supports_arrow_function();
597 let runtime_requirements = compilation
598 .cgc_runtime_requirements_artifact
599 .get(chunk_ukey)
600 .copied()
601 .unwrap_or_default();
602 let has_bootstrap_runtime_context = runtime_requirements.needs_bootstrap_runtime_context();
603 let mut chunk_init_fragments = ChunkInitFragments::default();
604 let iife = compilation.options.output.iife;
605 let mut all_strict = compilation.options.output.module;
606 let RenderBootstrapResult {
607 header,
608 startup,
609 allow_inline_startup,
610 } = Self::render_rspack_bootstrap(chunk_ukey, compilation, runtime_template).await?;
611 let module_graph = &compilation.get_module_graph();
612 let all_modules = compilation
613 .build_chunk_graph_artifact
614 .chunk_graph
615 .get_chunk_modules_by_source_type(chunk_ukey, SourceType::JavaScript, module_graph);
616 let has_entry_modules =
617 chunk.has_entry_module(&compilation.build_chunk_graph_artifact.chunk_graph);
618 let inlined_modules = if allow_inline_startup && has_entry_modules {
619 Some(
620 compilation
621 .build_chunk_graph_artifact
622 .chunk_graph
623 .get_chunk_entry_modules_with_chunk_group_iterable(chunk_ukey),
624 )
625 } else {
626 None
627 };
628 let mut sources = ConcatSource::default();
629 if iife {
630 sources.add(RawStringSource::from(if supports_arrow_function {
631 "(() => {\n"
632 } else {
633 "(function() {\n"
634 }));
635 }
636 if !all_strict && all_modules.iter().all(|m| m.build_info().strict) {
637 if let Some(strict_bailout) = hooks
638 .strict_runtime_bailout
639 .call(compilation, chunk_ukey)
640 .await?
641 {
642 sources.add(RawStringSource::from(format!(
643 "// runtime can't be in strict mode because {strict_bailout}.\n"
644 )));
645 } else {
646 all_strict = true;
647 sources.add(RawStringSource::from_static("\"use strict\";\n"));
648 }
649 }
650
651 let chunk_modules: Vec<&dyn Module> = if let Some(inlined_modules) = inlined_modules {
652 all_modules
653 .clone()
654 .into_iter()
655 .filter(|m| !inlined_modules.contains_key(&m.identifier()))
656 .collect::<Vec<_>>()
657 } else {
658 all_modules.clone()
659 };
660
661 let chunk_modules_result = render_chunk_modules(
662 compilation,
663 chunk_ukey,
664 &chunk_modules,
665 all_strict,
666 output_path,
667 &hooks,
668 runtime_template,
669 )
670 .await?;
671 let has_chunk_modules_result = chunk_modules_result.is_some();
672 if has_chunk_modules_result
673 || runtime_requirements.contains(RuntimeGlobals::MODULE_FACTORIES)
674 || runtime_requirements.contains(RuntimeGlobals::MODULE_FACTORIES_ADD_ONLY)
675 || runtime_requirements.contains(RuntimeGlobals::REQUIRE)
676 {
677 let chunk_modules_source =
678 if let Some((chunk_modules_source, fragments)) = chunk_modules_result {
679 chunk_init_fragments.extend(fragments);
680 chunk_modules_source
681 } else {
682 RawStringSource::from_static("{}").boxed()
683 };
684 sources.add(RawStringSource::from(format!(
685 "var {} = (",
686 runtime_template.render_runtime_variable(&RuntimeVariable::Modules)
687 )));
688 sources.add(chunk_modules_source);
689 sources.add(RawStringSource::from_static(");\n"));
690 }
691 if !header.is_empty() {
692 let mut header = header.join("\n");
693 header.push('\n');
694 sources.add(RawStringSource::from(header));
695 }
696 if compilation
697 .build_chunk_graph_artifact
698 .chunk_graph
699 .has_chunk_runtime_modules(chunk_ukey)
700 {
701 sources.add(render_runtime_modules(compilation, chunk_ukey, runtime_template).await?);
702 } else if runtime_template.uses_runtime_context() && !has_bootstrap_runtime_context {
703 sources.add(RawStringSource::from(render_runtime_context_declaration(
704 runtime_template,
705 )));
706 }
707 if let Some(inlined_modules) = inlined_modules {
708 let last_entry_module = inlined_modules
709 .keys()
710 .next_back()
711 .expect("should have last entry module");
712 let mut startup_sources = ConcatSource::default();
713
714 if runtime_requirements.contains(RuntimeGlobals::EXPORTS) {
715 startup_sources.add(RawStringSource::from(format!(
716 "var {} = {{}};\n",
717 runtime_template.render_runtime_globals(&RuntimeGlobals::EXPORTS)
718 )));
719 }
720
721 let renamed_inline_modules = if compilation.options.optimization.avoid_entry_iife {
722 self
723 .get_renamed_inline_module(
724 &all_modules,
725 inlined_modules,
726 compilation,
727 chunk_ukey,
728 all_strict,
729 has_chunk_modules_result,
730 output_path,
731 &hooks,
732 runtime_template,
733 )
734 .await?
735 } else {
736 None
737 };
738
739 for (m_identifier, _) in inlined_modules {
740 let m = module_graph
741 .module_by_identifier(m_identifier)
742 .expect("should have module");
743 let Some((mut rendered_module, fragments, additional_fragments)) = render_module(
744 compilation,
745 chunk_ukey,
746 m.as_ref(),
747 all_strict,
748 false,
749 output_path,
750 &hooks,
751 runtime_template,
752 )
753 .await?
754 else {
755 continue;
756 };
757
758 if let Some(renamed_inline_modules) = &renamed_inline_modules
759 && renamed_inline_modules.contains_key(m_identifier)
760 && let Some(source) = renamed_inline_modules.get(m_identifier)
761 {
762 rendered_module = source.clone();
763 };
764
765 chunk_init_fragments.extend(fragments);
766 chunk_init_fragments.extend(additional_fragments);
767 let inner_strict = !all_strict && m.build_info().strict;
768 let module_runtime_requirements =
769 ChunkGraph::get_module_runtime_requirements(compilation, *m_identifier, chunk.runtime());
770 let exports = module_runtime_requirements
771 .map(|r| r.contains(RuntimeGlobals::EXPORTS))
772 .unwrap_or_default();
773 let exports_argument = m.get_exports_argument();
774 let rspack_exports_argument = matches!(exports_argument, ExportsArgument::RspackExports);
775 let rspack_exports = exports && rspack_exports_argument;
776 let iife: Option<Cow<str>> = if inner_strict {
777 Some("it needs to be in strict mode.".into())
778 } else if inlined_modules.len() > 1 {
779 Some("it needs to be isolated against other entry modules.".into())
780 } else if has_chunk_modules_result && renamed_inline_modules.is_none() {
781 Some("it needs to be isolated against other modules in the chunk.".into())
782 } else if exports && !rspack_exports {
783 Some(
784 format!(
785 "it uses a non-standard name for the exports ({}).",
786 runtime_template.render_exports_argument(exports_argument)
787 )
788 .into(),
789 )
790 } else {
791 hooks
792 .embed_in_runtime_bailout
793 .call(compilation, m, chunk)
794 .await?
795 .map(|s| s.into())
796 };
797 let footer;
798 if let Some(iife) = iife {
799 startup_sources.add(RawStringSource::from(format!(
800 "// This entry needs to be wrapped in an IIFE because {iife}\n"
801 )));
802 if supports_arrow_function {
803 startup_sources.add(RawStringSource::from_static("(() => {\n"));
804 footer = "\n})();\n\n";
805 } else {
806 startup_sources.add(RawStringSource::from_static("!function() {\n"));
807 footer = "\n}();\n";
808 }
809 if inner_strict {
810 startup_sources.add(RawStringSource::from_static("\"use strict\";\n"));
811 }
812 } else {
813 footer = "\n";
814 }
815 if exports {
816 let exports_argument = runtime_template.render_exports_argument(exports_argument);
817 if m_identifier != last_entry_module {
818 startup_sources.add(RawStringSource::from(format!(
819 "var {exports_argument} = {{}};\n"
820 )));
821 } else if !rspack_exports_argument {
822 startup_sources.add(RawStringSource::from(format!(
823 "var {exports_argument} = {};\n",
824 runtime_template.render_runtime_globals(&RuntimeGlobals::EXPORTS)
825 )));
826 }
827 }
828 startup_sources.add(rendered_module);
829 startup_sources.add(RawStringSource::from(footer));
830 }
831 if runtime_requirements.contains(RuntimeGlobals::ON_CHUNKS_LOADED) {
832 startup_sources.add(RawStringSource::from(format!(
833 "{} = {}({});\n",
834 runtime_template.render_runtime_globals(&RuntimeGlobals::EXPORTS),
835 runtime_template.render_runtime_globals(&RuntimeGlobals::ON_CHUNKS_LOADED),
836 runtime_template.render_runtime_globals(&RuntimeGlobals::EXPORTS),
837 )));
838 }
839 let mut render_source = RenderSource {
840 source: startup_sources.boxed(),
841 };
842 hooks
843 .render_startup
844 .call(
845 compilation,
846 chunk_ukey,
847 last_entry_module,
848 &mut render_source,
849 runtime_template,
850 )
851 .await?;
852 sources.add(render_source.source);
853 } else if let Some(last_entry_module) = compilation
854 .build_chunk_graph_artifact
855 .chunk_graph
856 .get_chunk_entry_modules_with_chunk_group_iterable(chunk_ukey)
857 .keys()
858 .next_back()
859 {
860 let mut render_source = RenderSource {
861 source: RawStringSource::from(startup.join("\n") + "\n").boxed(),
862 };
863 hooks
864 .render_startup
865 .call(
866 compilation,
867 chunk_ukey,
868 last_entry_module,
869 &mut render_source,
870 runtime_template,
871 )
872 .await?;
873 sources.add(render_source.source);
874 }
875 if has_entry_modules
876 && runtime_requirements.contains(RuntimeGlobals::RETURN_EXPORTS_FROM_RUNTIME)
877 {
878 sources.add(RawStringSource::from(format!(
879 "return {};\n",
880 runtime_template.render_runtime_variable(&RuntimeVariable::Exports)
881 )));
882 }
883 if iife {
884 sources.add(RawStringSource::from_static("})()\n"));
885 }
886 let final_source = render_init_fragments(
887 sources.boxed(),
888 chunk_init_fragments,
889 &mut ChunkRenderContext {},
890 )?;
891 let mut render_source = RenderSource {
892 source: final_source,
893 };
894 hooks
895 .render
896 .call(
897 compilation,
898 chunk_ukey,
899 &mut render_source,
900 runtime_template,
901 )
902 .await?;
903 Ok(if iife {
904 ConcatSource::new([
905 render_source.source,
906 RawStringSource::from_static(";").boxed(),
907 ])
908 .boxed()
909 } else {
910 render_source.source
911 })
912 }
913}