1#![cfg_attr(
5 any(not(any(feature = "plugin")), target_arch = "wasm32"),
6 allow(unused)
7)]
8
9use serde::{Deserialize, Serialize};
10use swc_common::errors::HANDLER;
11use swc_ecma_ast::Pass;
12#[cfg(feature = "plugin")]
13use swc_ecma_ast::*;
14use swc_ecma_visit::{fold_pass, noop_fold_type, Fold};
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
24#[serde(deny_unknown_fields, rename_all = "camelCase")]
25pub struct PluginConfig(pub String, pub serde_json::Value);
26
27pub fn plugins(
28 configured_plugins: Option<Vec<PluginConfig>>,
29 metadata_context: std::sync::Arc<swc_common::plugin::metadata::TransformPluginMetadataContext>,
30 comments: Option<swc_common::comments::SingleThreadedComments>,
31 source_map: std::sync::Arc<swc_common::SourceMap>,
32 unresolved_mark: swc_common::Mark,
33) -> impl Pass {
34 fold_pass(RustPlugins {
35 plugins: configured_plugins,
36 metadata_context,
37 comments,
38 source_map,
39 unresolved_mark,
40 })
41}
42
43struct RustPlugins {
44 plugins: Option<Vec<PluginConfig>>,
45 metadata_context: std::sync::Arc<swc_common::plugin::metadata::TransformPluginMetadataContext>,
46 comments: Option<swc_common::comments::SingleThreadedComments>,
47 source_map: std::sync::Arc<swc_common::SourceMap>,
48 unresolved_mark: swc_common::Mark,
49}
50
51impl RustPlugins {
52 #[cfg(feature = "plugin")]
53 fn apply(&mut self, n: Program) -> Result<Program, anyhow::Error> {
54 use anyhow::Context;
55 if self.plugins.is_none() || self.plugins.as_ref().unwrap().is_empty() {
56 return Ok(n);
57 }
58
59 let filename = self.metadata_context.filename.clone();
60
61 if cfg!(feature = "manual-tokio-runtime") {
62 self.apply_inner(n)
63 } else {
64 let fut = async move { self.apply_inner(n) };
65 if let Ok(handle) = tokio::runtime::Handle::try_current() {
66 handle.block_on(fut)
67 } else {
68 tokio::runtime::Runtime::new().unwrap().block_on(fut)
69 }
70 }
71 .with_context(|| format!("failed to invoke plugin on '{filename:?}'"))
72 }
73
74 #[tracing::instrument(level = "info", skip_all, name = "apply_plugins")]
75 #[cfg(all(feature = "plugin", not(target_arch = "wasm32")))]
76 fn apply_inner(&mut self, n: Program) -> Result<Program, anyhow::Error> {
77 use anyhow::Context;
78 use swc_common::plugin::serialized::PluginSerializedBytes;
79
80 let should_enable_comments_proxy = self.comments.is_some();
82
83 swc_plugin_proxy::COMMENTS.set(
85 &swc_plugin_proxy::HostCommentsStorage {
86 inner: self.comments.clone(),
87 },
88 || {
89 let span = tracing::span!(tracing::Level::INFO, "serialize_program").entered();
90 let program = swc_common::plugin::serialized::VersionedSerializable::new(n);
91 let mut serialized = PluginSerializedBytes::try_serialize(&program)?;
92 drop(span);
93
94 if let Some(plugins) = &mut self.plugins {
101 for p in plugins.drain(..) {
102 let plugin_module_bytes = crate::config::PLUGIN_MODULE_CACHE
103 .inner
104 .get()
105 .unwrap()
106 .lock()
107 .get(&p.0)
108 .expect("plugin module should be loaded");
109
110 let plugin_name = plugin_module_bytes.get_module_name().to_string();
111 let runtime = swc_plugin_runner::wasix_runtime::build_wasi_runtime(
112 crate::config::PLUGIN_MODULE_CACHE
113 .inner
114 .get()
115 .unwrap()
116 .lock()
117 .get_fs_cache_root()
118 .map(|v| std::path::PathBuf::from(v)),
119 );
120 let mut transform_plugin_executor =
121 swc_plugin_runner::create_plugin_transform_executor(
122 &self.source_map,
123 &self.unresolved_mark,
124 &self.metadata_context,
125 plugin_module_bytes,
126 Some(p.1),
127 runtime,
128 );
129
130 let span = tracing::span!(
131 tracing::Level::INFO,
132 "execute_plugin_runner",
133 plugin_module = p.0.as_str()
134 )
135 .entered();
136
137 serialized = transform_plugin_executor
138 .transform(&serialized, Some(should_enable_comments_proxy))
139 .with_context(|| {
140 format!(
141 "failed to invoke `{}` as js transform plugin at {}",
142 &p.0, plugin_name
143 )
144 })?;
145 drop(span);
146 }
147 }
148
149 serialized.deserialize().map(|v| v.into_inner())
152 },
153 )
154 }
155
156 #[cfg(all(feature = "plugin", target_arch = "wasm32"))]
157 #[tracing::instrument(level = "info", skip_all)]
158 fn apply_inner(&mut self, n: Program) -> Result<Program, anyhow::Error> {
159 n
161 }
162}
163
164impl Fold for RustPlugins {
165 noop_fold_type!();
166
167 #[cfg(feature = "plugin")]
168 fn fold_module(&mut self, n: Module) -> Module {
169 match self.apply(Program::Module(n)) {
170 Ok(program) => program.expect_module(),
171 Err(err) => {
172 HANDLER.with(|handler| {
173 handler.err(&err.to_string());
174 });
175 Module::default()
176 }
177 }
178 }
179
180 #[cfg(feature = "plugin")]
181 fn fold_script(&mut self, n: Script) -> Script {
182 match self.apply(Program::Script(n)) {
183 Ok(program) => program.expect_script(),
184 Err(err) => {
185 HANDLER.with(|handler| {
186 handler.err(&err.to_string());
187 });
188 Script::default()
189 }
190 }
191 }
192}