rspack_plugin_mf 0.100.0

rspack module federation plugin
Documentation
use std::sync::Arc;

use rspack_core::{
  BoxModule, ChunkUkey, Compilation, CompilationParams, CompilationRuntimeRequirementInTree,
  CompilerCompilation, DependencyType, ExternalType, ModuleExt, ModuleFactoryCreateData,
  NormalModuleFactoryFactorize, Plugin, RuntimeGlobals, RuntimeModule,
};
use rspack_error::Result;
use rspack_hook::{plugin, plugin_hook};
use rspack_util::itoa;

use super::{
  fallback_module_factory::FallbackModuleFactory, remote_module::RemoteModule,
  remote_runtime_module::RemoteRuntimeModule,
};
use crate::ShareScope;

#[derive(Debug)]
pub struct ContainerReferencePluginOptions {
  pub remote_type: ExternalType,
  pub remotes: Vec<(String, RemoteOptions)>,
  pub share_scope: Option<ShareScope>,
  pub enhanced: bool,
}

#[derive(Debug)]
pub struct RemoteOptions {
  pub external: Vec<String>,
  pub share_scope: ShareScope,
}

#[plugin]
#[derive(Debug)]
pub struct ContainerReferencePlugin {
  options: ContainerReferencePluginOptions,
}

impl ContainerReferencePlugin {
  pub fn new(options: ContainerReferencePluginOptions) -> Self {
    Self::new_inner(options)
  }
}

#[plugin_hook(CompilerCompilation for ContainerReferencePlugin)]
async fn compilation(
  &self,
  compilation: &mut Compilation,
  params: &mut CompilationParams,
) -> Result<()> {
  compilation.set_dependency_factory(
    DependencyType::RemoteToExternal,
    params.normal_module_factory.clone(),
  );
  compilation.set_dependency_factory(
    DependencyType::RemoteToFallbackItem,
    params.normal_module_factory.clone(),
  );
  compilation.set_dependency_factory(
    DependencyType::RemoteToFallback,
    Arc::new(FallbackModuleFactory),
  );
  Ok(())
}

#[plugin_hook(NormalModuleFactoryFactorize for ContainerReferencePlugin)]
async fn factorize(&self, data: &mut ModuleFactoryCreateData) -> Result<Option<BoxModule>> {
  let request = &data.request;
  if !request.contains('!') {
    for (key, config) in &self.options.remotes {
      let key_len = key.len();
      if request.starts_with(key)
        && (request.len() == key_len || request[key_len..].starts_with('/'))
      {
        let internal_request = &request[key_len..];
        let remote = RemoteModule::new(
          request.to_owned(),
          config
            .external
            .iter()
            .enumerate()
            .map(|(i, e)| {
              if let Some(stripped) = e.strip_prefix("internal ") {
                stripped.to_string()
              } else {
                let fallback_suffix = if i > 0 {
                  let mut i_buffer = itoa::Buffer::new();
                  let i_str = i_buffer.format(i);
                  format!("/fallback-{i_str}")
                } else {
                  Default::default()
                };
                format!("webpack/container/reference/{key}{fallback_suffix}")
              }
            })
            .collect(),
          format!(".{internal_request}"),
          config.share_scope.clone(),
          key.clone(),
        )
        .boxed();
        return Ok(Some(remote));
      }
    }
  }
  Ok(None)
}

#[plugin_hook(CompilationRuntimeRequirementInTree for ContainerReferencePlugin)]
async fn runtime_requirements_in_tree(
  &self,
  compilation: &Compilation,
  chunk_ukey: &ChunkUkey,
  _all_runtime_requirements: &RuntimeGlobals,
  runtime_requirements: &RuntimeGlobals,
  _runtime_requirements_mut: &mut RuntimeGlobals,
  runtime_modules_to_add: &mut Vec<(ChunkUkey, Box<dyn RuntimeModule>)>,
) -> Result<Option<()>> {
  if runtime_requirements.contains(RuntimeGlobals::ENSURE_CHUNK_HANDLERS) {
    runtime_modules_to_add.push((
      *chunk_ukey,
      Box::new(RemoteRuntimeModule::new(
        &compilation.runtime_template,
        self.options.enhanced,
      )),
    ));
  }
  Ok(None)
}

impl Plugin for ContainerReferencePlugin {
  fn name(&self) -> &'static str {
    "rspack.ContainerReferencePlugin"
  }

  fn apply(&self, ctx: &mut rspack_core::ApplyContext<'_>) -> Result<()> {
    ctx.compiler_hooks.compilation.tap(compilation::new(self));
    ctx
      .normal_module_factory_hooks
      .factorize
      .tap(factorize::new(self));
    ctx
      .compilation_hooks
      .runtime_requirement_in_tree
      .tap(runtime_requirements_in_tree::new(self));
    Ok(())
  }
}