rspack_plugin_javascript 0.100.0-rc.2

rspack javascript plugin
Documentation
use itertools::Itertools;
use rspack_cacheable::{
  cacheable, cacheable_dyn,
  with::{AsPreset, AsVec},
};
use rspack_core::{
  DependencyCodeGeneration, DependencyTemplate, DependencyTemplateType, ExportProvided,
  ExportsInfoGetter, GetUsedNameParam, PrefetchExportsInfoMode, TemplateContext,
  TemplateReplaceSource, UsageState, UsedExports, UsedName,
};
use swc_core::ecma::atoms::Atom;

#[cacheable]
#[derive(Debug, Clone)]
pub struct ExportInfoDependency {
  start: u32,
  end: u32,
  #[cacheable(with=AsVec<AsPreset>)]
  export_name: Vec<Atom>,
  #[cacheable(with=AsPreset)]
  property: Atom,
}

impl ExportInfoDependency {
  pub fn new(start: u32, end: u32, export_name: Vec<Atom>, property: Atom) -> Self {
    Self {
      start,
      end,
      export_name,
      property,
    }
  }
}

#[cacheable_dyn]
impl DependencyCodeGeneration for ExportInfoDependency {
  fn dependency_template(&self) -> Option<DependencyTemplateType> {
    Some(ExportInfoDependencyTemplate::template_type())
  }
}

impl ExportInfoDependency {
  fn get_property(&self, context: &TemplateContext) -> Option<String> {
    let TemplateContext {
      compilation,
      module,
      runtime,
      ..
    } = context;
    let export_name = &self.export_name;
    let prop = &self.property;
    let module_identifier = module.identifier();

    if export_name.is_empty() && prop == "usedExports" {
      let exports_info = compilation
        .exports_info_artifact
        .get_prefetched_exports_info(&module_identifier, PrefetchExportsInfoMode::Default);
      let used_exports = exports_info.get_used_exports(*runtime);
      return Some(match used_exports {
        UsedExports::Unknown => "null".to_owned(),
        UsedExports::UsedNamespace(value) => value.to_string(),
        UsedExports::UsedNames(exports) => {
          format!(
            r#"[{}]"#,
            exports
              .iter()
              .map(|x| format!(r#""{x}""#))
              .collect_vec()
              .join(",")
          )
        }
      });
    }

    let exports_info = compilation
      .exports_info_artifact
      .get_prefetched_exports_info(
        &module_identifier,
        PrefetchExportsInfoMode::Nested(export_name),
      );

    match prop.to_string().as_str() {
      "canMangle" => {
        let can_mangle = if let Some(export_info) =
          exports_info.get_read_only_export_info_recursive(export_name)
        {
          export_info.can_mangle()
        } else {
          exports_info.other_exports_info().can_mangle()
        };
        can_mangle.map(|v| v.to_string())
      }
      "canInline" => {
        let used_name = ExportsInfoGetter::get_used_name(
          GetUsedNameParam::WithNames(&exports_info),
          *runtime,
          export_name,
        );
        Some(matches!(used_name, Some(UsedName::Inlined(_))).to_string())
      }
      "used" => {
        let used = exports_info.get_used(export_name, *runtime);
        Some((!matches!(used, UsageState::Unused)).to_string())
      }
      "useInfo" => {
        let used_state = exports_info.get_used(export_name, *runtime);
        Some(
          (match used_state {
            UsageState::Used => "true",
            UsageState::OnlyPropertiesUsed => "true",
            UsageState::Unused => "false",
            UsageState::NoInfo => "undefined",
            UsageState::Unknown => "null",
          })
          .to_owned(),
        )
      }
      "provideInfo" => exports_info
        .is_export_provided(export_name)
        .map(|provided| {
          (match provided {
            ExportProvided::Provided => "true",
            ExportProvided::NotProvided => "false",
            ExportProvided::Unknown => "null",
          })
          .to_owned()
        }),
      _ => None,
    }
  }
}

#[cacheable]
#[derive(Debug, Clone, Default)]
pub struct ExportInfoDependencyTemplate;

impl ExportInfoDependencyTemplate {
  pub fn template_type() -> DependencyTemplateType {
    DependencyTemplateType::Custom("ExportInfoDependency")
  }
}

impl DependencyTemplate for ExportInfoDependencyTemplate {
  fn render(
    &self,
    dep: &dyn DependencyCodeGeneration,
    source: &mut TemplateReplaceSource,
    code_generatable_context: &mut TemplateContext,
  ) {
    let dep = dep
      .as_any()
      .downcast_ref::<ExportInfoDependency>()
      .expect("ExportInfoDependencyTemplate should be used for ExportInfoDependency");

    let value = dep.get_property(code_generatable_context);
    source.replace(
      dep.start,
      dep.end,
      value.unwrap_or("undefined".to_string()),
      None,
    );
  }
}