rspack_plugin_rslib 0.102.6

Rslib native plugin
Documentation
use std::sync::Arc;

use rspack_core::{
  BuildModuleGraphArtifact, Dependency, DependencyCodeGeneration, DependencyTemplate,
  ExternalModule, JavascriptParserUrl, TemplateContext, TemplateReplaceSource,
};
use rspack_plugin_javascript::dependency::URLDependency;

fn should_cutout_url_external(
  cutout_all_externals: bool,
  output_module: bool,
  url_dependency: &URLDependency,
  external_module: &ExternalModule,
) -> bool {
  if !output_module {
    return false;
  }

  if !matches!(
    url_dependency.url_mode(),
    Some(JavascriptParserUrl::NewUrlRelative)
  ) {
    return false;
  }

  let request = &external_module.get_request().primary;
  if !request.starts_with("./") && !request.starts_with("../") {
    return false;
  }

  cutout_all_externals || matches!(external_module.resolve_external_type(), "import" | "module")
}

pub fn cutout_url_externals(
  cutout_all_externals: bool,
  output_module: bool,
  build_module_graph_artifact: &mut BuildModuleGraphArtifact,
) {
  let mg = build_module_graph_artifact.get_module_graph();
  let mut connections_to_disable = Vec::new();

  for (_, module) in mg.modules() {
    for dependency in module.get_dependencies() {
      let Some(url_dependency) = dependency.as_any().downcast_ref::<URLDependency>() else {
        continue;
      };

      let Some(connection) = mg.connection_by_dependency_id(dependency.id()) else {
        continue;
      };
      let Some(module) = mg.module_by_identifier(connection.module_identifier()) else {
        continue;
      };
      let Some(external_module) = module.as_external_module() else {
        continue;
      };

      if should_cutout_url_external(
        cutout_all_externals,
        output_module,
        url_dependency,
        external_module,
      ) {
        connections_to_disable.push(*dependency.id());
      }
    }
  }

  let mg = build_module_graph_artifact.get_module_graph_mut();
  for dep_id in connections_to_disable {
    let connection = mg
      .connection_by_dependency_id_mut(&dep_id)
      .expect("url external should have a module graph connection");
    connection.force_inactive();
  }
}

fn render_url_external_module(
  url_dependency: &URLDependency,
  external_module: &ExternalModule,
  source: &mut TemplateReplaceSource,
) {
  let request = rspack_util::json_stringify_str(&external_module.get_request().primary);
  url_dependency.replace_request(source, request);
}

#[derive(Debug)]
pub struct ExternalURLDependencyTemplate {
  pub cutout_all_externals: bool,
  pub template: Option<Arc<dyn DependencyTemplate>>,
}

impl DependencyTemplate for ExternalURLDependencyTemplate {
  fn render(
    &self,
    dep: &dyn DependencyCodeGeneration,
    source: &mut TemplateReplaceSource,
    code_generatable_context: &mut TemplateContext,
  ) {
    let url_dependency = dep
      .as_any()
      .downcast_ref::<URLDependency>()
      .expect("ExternalURLDependencyTemplate should be used for URLDependency");
    let compilation = code_generatable_context.compilation;
    let module_graph = compilation.get_module_graph();
    let external_module = module_graph
      .module_identifier_by_dependency_id(url_dependency.id())
      .and_then(|module_id| module_graph.module_by_identifier(module_id))
      .and_then(|module| module.as_external_module());

    if let Some(external_module) = external_module
      && should_cutout_url_external(
        self.cutout_all_externals,
        compilation.options.output.module,
        url_dependency,
        external_module,
      )
    {
      render_url_external_module(url_dependency, external_module, source);
      return;
    }

    if let Some(template) = &self.template {
      template.render(dep, source, code_generatable_context);
    }
  }
}