rspack_plugin_javascript 0.100.0-rc.3

rspack javascript plugin
Documentation
mod parser;
mod utils;
mod walk_data;

use std::sync::Arc;

use parser::DefineParserPlugin;
use rspack_core::{
  Compilation, CompilationParams, CompilerCompilation, ModuleType, NormalModuleFactoryParser,
  ParserAndGenerator, ParserOptions, Plugin,
};
use rspack_error::{Diagnostic, Error, Result};
use rspack_hook::{plugin, plugin_hook};
use rustc_hash::FxHashMap;
use serde_json::Value;

use self::walk_data::WalkData;
use crate::parser_and_generator::JavaScriptParserAndGenerator;

const VALUE_DEP_PREFIX: &str = "rspack/DefinePlugin ";

#[derive(Debug)]
struct ConflictingValuesError(String, String, String);

impl ConflictingValuesError {
  fn into_diagnostic(self) -> Diagnostic {
    Error::warning(format!(
      "DefinePlugin:\nConflicting values for '{}' ({} !== {})",
      self.0, self.1, self.2
    ))
    .into()
  }
}

pub type DefineValue = FxHashMap<String, Value>;

#[plugin]
#[derive(Debug)]
pub struct DefinePlugin {
  walk_data: Arc<WalkData>,
}

impl DefinePlugin {
  pub fn new(definitions: DefineValue) -> Self {
    Self::new_inner(Arc::new(WalkData::new(&definitions)))
  }
}

#[plugin_hook(CompilerCompilation for DefinePlugin, tracing=false)]
async fn compilation(
  &self,
  compilation: &mut Compilation,
  _params: &mut CompilationParams,
) -> Result<()> {
  compilation.extend_diagnostics(self.walk_data.diagnostics.clone());
  for (key, value) in self.walk_data.tiling_definitions.iter() {
    let cache_key = format!("{VALUE_DEP_PREFIX}{key}");
    if let Some(prev) = compilation.value_cache_versions.get(&cache_key)
      && prev != value
    {
      compilation.push_diagnostic(
        ConflictingValuesError(key.clone(), prev.clone(), value.clone()).into_diagnostic(),
      );
    } else {
      compilation
        .value_cache_versions
        .insert(cache_key, value.clone());
    }
  }

  Ok(())
}

#[plugin_hook(NormalModuleFactoryParser for DefinePlugin, tracing=false)]
async fn nmf_parser(
  &self,
  module_type: &ModuleType,
  parser: &mut Box<dyn ParserAndGenerator>,
  _parser_options: Option<&ParserOptions>,
) -> Result<()> {
  if module_type.is_js_like()
    && let Some(parser) = parser.downcast_mut::<JavaScriptParserAndGenerator>()
  {
    parser.add_parser_plugin(Box::new(DefineParserPlugin::new(self.walk_data.clone())));
  }
  Ok(())
}

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

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