Skip to main content

rspack_plugin_javascript/plugin/
runtime_context.rs

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}