rspack_plugin_javascript 0.101.0-rc.0

rspack javascript plugin
Documentation
use rspack_core::{
  Compilation, DependencyId, ExportsInfoArtifact, GetTargetResult, ModuleGraph,
  ModuleGraphConnection, ModuleIdentifier, ResolvedExportInfoTarget, RuntimeCondition, RuntimeSpec,
  UsageState, UsedByExports, UsedByExportsCondition, filter_runtime, get_target,
};
use swc_atoms::Atom;

pub mod plugin;
pub mod state;

fn module_has_side_effects_free_export(
  module_graph: &ModuleGraph,
  module_identifier: &ModuleIdentifier,
  atom: &Atom,
) -> Option<bool> {
  let module = module_graph.module_by_identifier(module_identifier)?;
  let side_effects_free = module.build_info().side_effects_free.as_ref()?;
  Some(side_effects_free.contains(atom))
}

pub fn deferred_pure_check_is_impure(
  module_graph: &ModuleGraph,
  exports_info_artifact: &ExportsInfoArtifact,
  dep_id: &DependencyId,
  atom: &Atom,
) -> bool {
  let Some(ref_module) = module_graph.module_identifier_by_dependency_id(dep_id) else {
    return true;
  };

  let target_exports_info = exports_info_artifact.get_exports_info_data(ref_module);
  let target_export_info = target_exports_info.get_export_info_without_mut_module_graph(atom);
  let resolve_filter = |_: &ResolvedExportInfoTarget| true;

  let (ref_module_id, resolved_atom) = if let Some(GetTargetResult::Target(target)) = get_target(
    &target_export_info,
    module_graph,
    exports_info_artifact,
    &resolve_filter,
    &mut Default::default(),
  ) {
    let atom = if target.module == *ref_module {
      Some(atom.clone())
    } else {
      target
        .export
        .as_ref()
        .and_then(|export| export.first().cloned())
    };
    (target.module, atom)
  } else {
    (*ref_module, Some(atom.clone()))
  };

  if let Some(resolved_atom) = resolved_atom.as_ref()
    && let Some(side_effects_free) =
      module_has_side_effects_free_export(module_graph, &ref_module_id, resolved_atom)
  {
    return !side_effects_free;
  }

  if let Some(resolved_module) = module_graph.get_resolved_module(dep_id)
    && resolved_module != &ref_module_id
    && let Some(side_effects_free) =
      module_has_side_effects_free_export(module_graph, resolved_module, atom)
  {
    return !side_effects_free;
  }

  true
}

pub fn has_impure_deferred_pure_checks(
  module_graph: &ModuleGraph,
  exports_info_artifact: &ExportsInfoArtifact,
  used_by_exports: &UsedByExports,
) -> bool {
  used_by_exports
    .deferred_pure_checks()
    .iter()
    .any(|deferred_check| {
      deferred_pure_check_is_impure(
        module_graph,
        exports_info_artifact,
        &deferred_check.dep_id,
        &deferred_check.atom,
      )
    })
}

pub(crate) fn runtime_condition_used_by_exports(
  compilation: &Compilation,
  module_identifier: &ModuleIdentifier,
  runtime: Option<&RuntimeSpec>,
  used_by_exports: Option<&UsedByExports>,
) -> RuntimeCondition {
  let Some(used_by_exports) = used_by_exports else {
    return RuntimeCondition::Boolean(true);
  };

  if has_impure_deferred_pure_checks(
    compilation.get_module_graph(),
    &compilation.exports_info_artifact,
    used_by_exports,
  ) {
    return RuntimeCondition::Boolean(true);
  }

  match used_by_exports.condition() {
    UsedByExportsCondition::Bool(used) => RuntimeCondition::Boolean(*used),
    UsedByExportsCondition::Set(used_by_exports) => {
      let exports_info = compilation
        .exports_info_artifact
        .get_exports_info_data(module_identifier);
      filter_runtime(runtime, |cur_runtime| {
        used_by_exports.iter().any(|name| {
          exports_info.get_used(
            &compilation.exports_info_artifact,
            std::slice::from_ref(name),
            cur_runtime,
          ) != UsageState::Unused
        })
      })
    }
  }
}

pub fn connection_active_used_by_exports(
  connection: &ModuleGraphConnection,
  runtime: Option<&RuntimeSpec>,
  mg: &ModuleGraph,
  exports_info_artifact: &ExportsInfoArtifact,
  used_by_exports: Option<&UsedByExports>,
) -> bool {
  let Some(used_by_exports) = used_by_exports.as_ref() else {
    return true;
  };
  if has_impure_deferred_pure_checks(mg, exports_info_artifact, used_by_exports) {
    return true;
  }
  let used_by_exports = match used_by_exports.condition() {
    UsedByExportsCondition::Set(used_by_exports) => used_by_exports,
    UsedByExportsCondition::Bool(used) => return *used,
  };
  let module_identifier = mg
    .get_parent_module(&connection.dependency_id)
    .expect("should have parent module");
  let exports_info = exports_info_artifact.get_exports_info_data(module_identifier);
  used_by_exports.iter().any(|name| {
    exports_info
      .named_exports(name)
      .unwrap_or_else(|| exports_info.other_exports_info())
      .get_used(runtime)
      != UsageState::Unused
  })
}