rspack_plugin_runtime 0.100.1

rspack runtime plugin
Documentation
use std::ptr::NonNull;

use rspack_core::{
  ChunkUkey, Compilation, RuntimeGlobals, RuntimeModule, RuntimeModuleGenerateContext,
  RuntimeTemplate, impl_runtime_module,
};

use crate::{
  CreateScriptData, RuntimeModuleChunkWrapper, RuntimePlugin, get_chunk_runtime_requirements,
};

#[impl_runtime_module]
#[derive(Debug)]
pub struct LoadScriptRuntimeModule {
  unique_name: String,
  with_create_script_url: bool,
  chunk_ukey: ChunkUkey,
}

impl LoadScriptRuntimeModule {
  pub fn new(
    runtime_template: &RuntimeTemplate,
    unique_name: String,
    with_create_script_url: bool,
    chunk_ukey: ChunkUkey,
  ) -> Self {
    Self::with_default(
      runtime_template,
      unique_name,
      with_create_script_url,
      chunk_ukey,
    )
  }
}

enum TemplateId {
  Raw,
  CreateScript,
}

#[async_trait::async_trait]
impl RuntimeModule for LoadScriptRuntimeModule {
  fn template(&self) -> Vec<(String, String)> {
    vec![
      (
        self.template_id(TemplateId::Raw),
        include_str!("runtime/load_script.ejs").to_string(),
      ),
      (
        self.template_id(TemplateId::CreateScript),
        include_str!("runtime/load_script_create_script.ejs").to_string(),
      ),
    ]
  }

  async fn generate(
    &self,
    context: &RuntimeModuleGenerateContext<'_>,
  ) -> rspack_error::Result<String> {
    let compilation = context.compilation;
    let runtime_template = context.runtime_template;
    let runtime_requirements = get_chunk_runtime_requirements(compilation, &self.chunk_ukey);
    let with_fetch_priority = runtime_requirements.contains(RuntimeGlobals::HAS_FETCH_PRIORITY);

    let unique_prefix = if self.unique_name.is_empty() {
      None
    } else {
      Some(format!(
        r#"var uniqueName = {};"#,
        rspack_util::json_stringify_str(&format!("{}:", self.unique_name))
      ))
    };

    let create_script_code = runtime_template.render(
      &self.template_id(TemplateId::CreateScript),
      Some(serde_json::json!({
        "_script_type": &compilation.options.output.script_type,
        "_unique_prefix": unique_prefix.is_some(),
        "_with_fetch_priority": with_fetch_priority,
        "_with_create_script_url": self.with_create_script_url,
        "_cross_origin": compilation.options.output.cross_origin_loading.to_string(),
        "_chunk_load_timeout": compilation.options.output.chunk_load_timeout.saturating_div(1000).to_string(),
      })),
    )?;

    let hooks = RuntimePlugin::get_compilation_hooks(compilation.id());
    let chunk_ukey = self.chunk_ukey;
    let res = hooks
      .borrow()
      .create_script
      .call(CreateScriptData {
        code: create_script_code,
        chunk: RuntimeModuleChunkWrapper {
          chunk_ukey,
          compilation_id: compilation.id(),
          compilation: NonNull::from(compilation),
        },
      })
      .await?;

    let render_source = runtime_template.render(
      &self.template_id(TemplateId::Raw),
      Some(serde_json::json!({
        "_unique_prefix": unique_prefix.unwrap_or_default(),
        "_create_script": res.code,
        "_chunk_load_timeout": compilation.options.output.chunk_load_timeout.to_string(),
        "_fetch_priority": if with_fetch_priority { ", fetchPriority" } else { "" },
      })),
    )?;

    Ok(render_source)
  }

  fn additional_runtime_requirements(&self, compilation: &Compilation) -> RuntimeGlobals {
    if compilation.options.output.trusted_types.is_some() {
      RuntimeGlobals::CREATE_SCRIPT_URL
    } else {
      RuntimeGlobals::default()
    }
  }
}

impl LoadScriptRuntimeModule {
  fn template_id(&self, id: TemplateId) -> String {
    let base_id = self.id.to_string();

    match id {
      TemplateId::Raw => base_id,
      TemplateId::CreateScript => format!("{base_id}_create_script"),
    }
  }
}