rspack_plugin_runtime 0.101.5

rspack runtime plugin
Documentation
use std::{ptr::NonNull, sync::LazyLock};

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

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

static LOAD_SCRIPT_TEMPLATE: &str = include_str!("runtime/load_script.ejs");
static LOAD_SCRIPT_CREATE_SCRIPT_TEMPLATE: &str =
  include_str!("runtime/load_script_create_script.ejs");
static LOAD_SCRIPT_RUNTIME_REQUIREMENTS: LazyLock<RuntimeModuleRuntimeRequirements> =
  LazyLock::new(|| extract_runtime_globals_from_ejs(LOAD_SCRIPT_TEMPLATE));
static LOAD_SCRIPT_CREATE_SCRIPT_RUNTIME_REQUIREMENTS: LazyLock<RuntimeModuleRuntimeRequirements> =
  LazyLock::new(|| extract_runtime_globals_from_ejs(LOAD_SCRIPT_CREATE_SCRIPT_TEMPLATE));

#[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 runtime_requirements(
    &self,
    _compilation: &Compilation,
  ) -> rspack_core::RuntimeModuleRuntimeRequirements {
    rspack_core::RuntimeModuleRuntimeRequirements {
      dependencies: {
        let mut requirements = LOAD_SCRIPT_RUNTIME_REQUIREMENTS.dependencies
          | LOAD_SCRIPT_CREATE_SCRIPT_RUNTIME_REQUIREMENTS.dependencies;
        if self.with_create_script_url {
          requirements.insert(RuntimeGlobals::CREATE_SCRIPT_URL);
        }
        requirements
      },
      weak: LOAD_SCRIPT_CREATE_SCRIPT_RUNTIME_REQUIREMENTS.weak,
      define: LOAD_SCRIPT_RUNTIME_REQUIREMENTS.define,
      ..Default::default()
    }
  }

  fn template(&self) -> Vec<(String, String)> {
    vec![
      (
        self.template_id(TemplateId::Raw),
        LOAD_SCRIPT_TEMPLATE.to_string(),
      ),
      (
        self.template_id(TemplateId::CreateScript),
        LOAD_SCRIPT_CREATE_SCRIPT_TEMPLATE.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)
  }
}

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"),
    }
  }
}