#![cfg_attr(any(not(feature = "plugin"), target_arch = "wasm32"), allow(unused))]
use serde::{Deserialize, Serialize};
#[cfg(feature = "plugin")]
use swc_ecma_ast::*;
use swc_ecma_loader::resolvers::{lru::CachingResolver, node::NodeModulesResolver};
#[cfg(not(feature = "plugin"))]
use swc_ecma_transforms::pass::noop;
use swc_ecma_visit::{noop_fold_type, Fold};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct PluginConfig(String, serde_json::Value);
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct PluginContext {
pub filename: Option<String>,
pub env_name: String,
}
pub fn plugins(
resolver: CachingResolver<NodeModulesResolver>,
config: crate::config::JscExperimental,
plugin_context: PluginContext,
) -> impl Fold {
#[cfg(feature = "plugin")]
{
RustPlugins {
resolver,
plugins: config.plugins,
plugin_context,
}
}
#[cfg(not(feature = "plugin"))]
{
noop()
}
}
struct RustPlugins {
resolver: CachingResolver<NodeModulesResolver>,
plugins: Option<Vec<PluginConfig>>,
plugin_context: PluginContext,
}
impl RustPlugins {
#[tracing::instrument(level = "info", skip_all, name = "apply_plugins")]
#[cfg(all(feature = "plugin", not(target_arch = "wasm32")))]
fn apply(&mut self, n: Program) -> Result<Program, anyhow::Error> {
use std::{path::PathBuf, sync::Arc};
use anyhow::Context;
use swc_common::{plugin::Serialized, FileName};
use swc_ecma_loader::resolve::Resolve;
let mut serialized = Serialized::serialize(&n)?;
if let Some(plugins) = &self.plugins {
for p in plugins {
let span = tracing::span!(
tracing::Level::INFO,
"serialize_context",
plugin_module = p.0.as_str()
);
let context_span_guard = span.enter();
let config_json = serde_json::to_string(&p.1)
.context("Failed to serialize plugin config as json")
.and_then(|value| Serialized::serialize(&value))?;
let context_json = serde_json::to_string(&self.plugin_context)
.context("Failed to serialize plugin context as json")
.and_then(|value| Serialized::serialize(&value))?;
let resolved_path = self
.resolver
.resolve(&FileName::Real(PathBuf::from(&p.0)), &p.0)?;
let path = if let FileName::Real(value) = resolved_path {
Arc::new(value)
} else {
anyhow::bail!("Failed to resolve plugin path: {:?}", resolved_path);
};
drop(context_span_guard);
let span = tracing::span!(
tracing::Level::INFO,
"execute_plugin_runner",
plugin_module = p.0.as_str()
);
let transform_span_guard = span.enter();
serialized = swc_plugin_runner::apply_transform_plugin(
&p.0,
&path,
&swc_plugin_runner::cache::PLUGIN_MODULE_CACHE,
serialized,
config_json,
context_json,
)?;
drop(transform_span_guard);
}
}
Serialized::deserialize(&serialized)
}
#[cfg(all(feature = "plugin", target_arch = "wasm32"))]
fn apply(&mut self, n: Program) -> Result<Program, anyhow::Error> {
Ok(n)
}
}
impl Fold for RustPlugins {
noop_fold_type!();
#[cfg(feature = "plugin")]
fn fold_module(&mut self, n: Module) -> Module {
self.apply(Program::Module(n))
.expect("failed to invoke plugin")
.expect_module()
}
#[cfg(feature = "plugin")]
fn fold_script(&mut self, n: Script) -> Script {
self.apply(Program::Script(n))
.expect("failed to invoke plugin")
.expect_script()
}
}