#![cfg_attr(
any(
not(any(feature = "plugin", feature = "plugin-bytecheck")),
target_arch = "wasm32"
),
allow(unused)
)]
use serde::{Deserialize, Serialize};
#[cfg(any(feature = "plugin", feature = "plugin-bytecheck"))]
use swc_ecma_ast::*;
use swc_ecma_loader::resolvers::{lru::CachingResolver, node::NodeModulesResolver};
#[cfg(not(any(feature = "plugin", feature = "plugin-bytecheck")))]
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);
#[cfg(any(feature = "plugin", feature = "plugin-bytecheck"))]
pub fn plugins(
configured_plugins: Option<Vec<PluginConfig>>,
metadata_context: std::sync::Arc<swc_common::plugin::metadata::TransformPluginMetadataContext>,
resolver: Option<CachingResolver<NodeModulesResolver>>,
comments: Option<swc_common::comments::SingleThreadedComments>,
source_map: std::sync::Arc<swc_common::SourceMap>,
unresolved_mark: swc_common::Mark,
) -> impl Fold {
{
RustPlugins {
plugins: configured_plugins,
metadata_context,
resolver,
comments,
source_map,
unresolved_mark,
}
}
}
#[cfg(not(any(feature = "plugin", feature = "plugin-bytecheck")))]
pub fn plugins() -> impl Fold {
noop()
}
struct RustPlugins {
plugins: Option<Vec<PluginConfig>>,
metadata_context: std::sync::Arc<swc_common::plugin::metadata::TransformPluginMetadataContext>,
resolver: Option<CachingResolver<NodeModulesResolver>>,
comments: Option<swc_common::comments::SingleThreadedComments>,
source_map: std::sync::Arc<swc_common::SourceMap>,
unresolved_mark: swc_common::Mark,
}
impl RustPlugins {
#[cfg(any(feature = "plugin", feature = "plugin-bytecheck"))]
fn apply(&mut self, n: Program) -> Result<Program, anyhow::Error> {
use anyhow::Context;
if self.plugins.is_none() || self.plugins.as_ref().unwrap().is_empty() {
return Ok(n);
}
self.apply_inner(n).with_context(|| {
format!(
"failed to invoke plugin on '{:?}'",
self.metadata_context.filename
)
})
}
#[tracing::instrument(level = "info", skip_all, name = "apply_plugins")]
#[cfg(all(
any(feature = "plugin", feature = "plugin-bytecheck"),
not(target_arch = "wasm32")
))]
fn apply_inner(&mut self, n: Program) -> Result<Program, anyhow::Error> {
use std::{path::PathBuf, sync::Arc};
use anyhow::Context;
use swc_common::{plugin::serialized::PluginSerializedBytes, FileName};
use swc_ecma_loader::resolve::Resolve;
let should_enable_comments_proxy = self.comments.is_some();
swc_plugin_proxy::COMMENTS.set(
&swc_plugin_proxy::HostCommentsStorage {
inner: self.comments.clone(),
},
|| {
let span = tracing::span!(tracing::Level::INFO, "serialize_program").entered();
let mut serialized_program = PluginSerializedBytes::try_serialize(&n)?;
drop(span);
if let Some(plugins) = &mut self.plugins {
for p in plugins.drain(..) {
let resolved_path = self
.resolver
.as_ref()
.expect("filesystem_cache should provide 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);
};
let mut transform_plugin_executor =
swc_plugin_runner::create_plugin_transform_executor(
&path,
&swc_plugin_runner::cache::PLUGIN_MODULE_CACHE,
&self.source_map,
&self.metadata_context,
Some(p.1),
)?;
if !transform_plugin_executor.is_transform_schema_compatible()? {
anyhow::bail!("Cannot execute incompatible plugin {}", &p.0);
}
let span = tracing::span!(
tracing::Level::INFO,
"execute_plugin_runner",
plugin_module = p.0.as_str()
)
.entered();
serialized_program = transform_plugin_executor
.transform(
&serialized_program,
self.unresolved_mark,
should_enable_comments_proxy,
)
.with_context(|| {
format!(
"failed to invoke `{}` as js transform plugin at {}",
&p.0,
path.display()
)
})?;
drop(span);
}
}
serialized_program.deserialize()
},
)
}
#[cfg(all(
any(feature = "plugin", feature = "plugin-bytecheck"),
target_arch = "wasm32"
))]
#[tracing::instrument(level = "info", skip_all)]
fn apply_inner(&mut self, n: Program) -> Result<Program, anyhow::Error> {
use std::{path::PathBuf, sync::Arc};
use anyhow::Context;
use swc_common::{
collections::AHashMap, plugin::serialized::PluginSerializedBytes, FileName,
};
use swc_ecma_loader::resolve::Resolve;
let should_enable_comments_proxy = self.comments.is_some();
swc_plugin_proxy::COMMENTS.set(
&swc_plugin_proxy::HostCommentsStorage {
inner: self.comments.clone(),
},
|| {
let mut serialized_program = PluginSerializedBytes::try_serialize(&n)?;
if let Some(plugins) = &mut self.plugins {
for p in plugins.drain(..) {
let mut transform_plugin_executor =
swc_plugin_runner::create_plugin_transform_executor(
&PathBuf::from(&p.0),
&swc_plugin_runner::cache::PLUGIN_MODULE_CACHE,
&self.source_map,
&self.metadata_context,
Some(p.1),
)?;
serialized_program = transform_plugin_executor
.transform(
&serialized_program,
self.unresolved_mark,
should_enable_comments_proxy,
)
.with_context(|| {
format!("failed to invoke `{}` as js transform plugin", &p.0)
})?;
}
}
serialized_program.deserialize()
},
)
}
}
impl Fold for RustPlugins {
noop_fold_type!();
#[cfg(any(feature = "plugin", feature = "plugin-bytecheck"))]
fn fold_module(&mut self, n: Module) -> Module {
self.apply(Program::Module(n))
.expect("failed to invoke plugin")
.expect_module()
}
#[cfg(any(feature = "plugin", feature = "plugin-bytecheck"))]
fn fold_script(&mut self, n: Script) -> Script {
self.apply(Program::Script(n))
.expect("failed to invoke plugin")
.expect_script()
}
}