Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/moody-houses-judge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
swc_core: minor
swc: minor
---

feat(es): Add Rust plugin host part for analysis API
7 changes: 5 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@
"crates/swc_ecma_transforms_proposal/tests/decorator-tests"
],
"eslint.enable": false,
"rust-analyzer.check.command": "clippy"
}
"rust-analyzer.check.command": "clippy",
"rust-analyzer.cargo.features": [
"plugin"
]
}
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion crates/swc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ jsonc-parser = { workspace = true, features = ["serde"] }
lru = { workspace = true }
once_cell = { workspace = true }
par-core = { workspace = true }
par-iter = { workspace = true }
parking_lot = { workspace = true }
pathdiff = { workspace = true }
regex = { workspace = true }
Expand All @@ -70,6 +71,7 @@ sourcemap = { workspace = true }
tracing = { workspace = true }
url = { workspace = true }


swc_atoms = { version = "5.0.0", path = "../swc_atoms" }
swc_cached = { version = "2.0.0", path = "../swc_cached" }
swc_common = { version = "8.0.1", path = "../swc_common", features = [
Expand Down Expand Up @@ -133,11 +135,12 @@ ansi_term = { workspace = true }
criterion = { workspace = true }
flate2 = { workspace = true }
humansize = { workspace = true }
rayon = { workspace = true }
walkdir = { workspace = true }


codspeed-criterion-compat = { workspace = true }
par-core = { workspace = true, features = ["chili"] }

swc_ecma_ast = { version = "8.1.0", path = "../swc_ecma_ast", features = [
"serde-impl",
] }
Expand Down
10 changes: 5 additions & 5 deletions crates/swc/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -624,10 +624,10 @@ impl Options {
for plugin_config in plugins.iter() {
let plugin_name = &plugin_config.0;

if !inner_cache.contains(&plugin_name) {
if !inner_cache.contains(plugin_name) {
let resolved_path = plugin_resolver.resolve(
&FileName::Real(PathBuf::from(&plugin_name)),
&plugin_name,
&FileName::Real(PathBuf::from(plugin_name)),
plugin_name,
)?;

let path = if let FileName::Real(value) = resolved_path.filename {
Expand All @@ -636,7 +636,7 @@ impl Options {
anyhow::bail!("Failed to resolve plugin path: {:?}", resolved_path);
};

inner_cache.store_bytes_from_path(&path, &plugin_name)?;
inner_cache.store_bytes_from_path(&path, plugin_name)?;
tracing::debug!("Initialized WASM plugin {plugin_name}");
}
}
Expand Down Expand Up @@ -1731,7 +1731,7 @@ impl GlobalPassOption {
}
}

fn default_env_name() -> String {
pub(crate) fn default_env_name() -> String {
if let Ok(v) = env::var("SWC_ENV") {
return v;
}
Expand Down
1 change: 1 addition & 0 deletions crates/swc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ mod builder;
pub mod config;
mod dropped_comments_preserver;
mod plugin;
mod wasm_analysis;
pub mod resolver {
use std::path::PathBuf;

Expand Down
2 changes: 1 addition & 1 deletion crates/swc/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ impl RustPlugins {
.unwrap()
.lock()
.get_fs_cache_root()
.map(|v| std::path::PathBuf::from(v)),
.map(std::path::PathBuf::from),
);
let mut transform_plugin_executor =
swc_plugin_runner::create_plugin_transform_executor(
Expand Down
136 changes: 136 additions & 0 deletions crates/swc/src/wasm_analysis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#![cfg(feature = "plugin")]

use std::sync::Arc;

use anyhow::{Context, Result};
use common::{
comments::SingleThreadedComments,
errors::Handler,
plugin::{metadata::TransformPluginMetadataContext, serialized::PluginSerializedBytes},
Mark, SourceFile, GLOBALS,
};
use par_iter::iter::{IntoParallelRefIterator, ParallelIterator};
use serde::Deserialize;
use swc_config::IsModule;
use swc_ecma_ast::EsVersion;
use swc_ecma_parser::Syntax;
use swc_ecma_transforms::resolver;

use crate::{plugin::PluginConfig, Compiler};

impl Compiler {
/// Run analysis using Wasm plugins.
pub fn run_wasm_analysis(
&self,
fm: Arc<SourceFile>,
handler: &Handler,
opts: &WasmAnalysisOptions,
comments: SingleThreadedComments,
) -> Result<String> {
self.run(|| {
GLOBALS.with(|globals| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();

let mut program = self.parse_js(
fm.clone(),
handler,
EsVersion::latest(),
opts.parser,
opts.module,
Some(&comments),
)?;

program.mutate(resolver(
unresolved_mark,
top_level_mark,
opts.parser.typescript(),
));

let serialized = {
let _span = tracing::span!(tracing::Level::INFO, "serialize_program").entered();
let program =
swc_common::plugin::serialized::VersionedSerializable::new(program);
PluginSerializedBytes::try_serialize(&program)?
};

let transform_metadata_context = Arc::new(TransformPluginMetadataContext::new(
Some(fm.name.to_string()),
crate::config::default_env_name(),
None,
));

let result = opts
.plugins
.par_iter()
.map(|p| {
GLOBALS.set(globals, || {
let plugin_module_bytes = crate::config::PLUGIN_MODULE_CACHE
.inner
.get()
.unwrap()
.lock()
.get(&p.0)
.expect("plugin module should be loaded");

let plugin_name = plugin_module_bytes.get_module_name().to_string();
let runtime = swc_plugin_runner::wasix_runtime::build_wasi_runtime(
crate::config::PLUGIN_MODULE_CACHE
.inner
.get()
.unwrap()
.lock()
.get_fs_cache_root()
.map(std::path::PathBuf::from),
);
let mut transform_plugin_executor =
swc_plugin_runner::create_plugin_transform_executor(
&self.cm,
&unresolved_mark,
&transform_metadata_context,
plugin_module_bytes,
Some(p.1.clone()),
runtime,
);

let span = tracing::span!(
tracing::Level::INFO,
"execute_plugin_runner",
plugin_module = p.0.as_str()
)
.entered();

let (result, output) = swc_transform_common::output::capture(|| {
transform_plugin_executor
.transform(&serialized, Some(true))
.with_context(|| {
format!(
"failed to invoke `{}` as js analysis plugin at {}",
&p.0, plugin_name
)
})
});
result?;
drop(span);

Ok(output)
})
})
.collect::<Result<Vec<_>>>()?;

serde_json::to_string(&result)
.map_err(|e| anyhow::anyhow!("Failed to serialize output: {e}"))
})
})
}
}

#[derive(Debug, Deserialize)]
pub struct WasmAnalysisOptions {
#[serde(default)]
pub parser: Syntax,
#[serde(default)]
pub module: IsModule,

pub plugins: Vec<PluginConfig>,
}
2 changes: 1 addition & 1 deletion crates/swc/tests/projects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{
};

use anyhow::Context;
use rayon::prelude::*;
use par_iter::prelude::*;
use swc::{
config::{
Config, FileMatcher, JsMinifyOptions, JscConfig, ModuleConfig, Options, Paths,
Expand Down
1 change: 1 addition & 0 deletions crates/swc_plugin_runner/benches/ecma_invoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::{
};

use codspeed_criterion_compat::{black_box, criterion_group, criterion_main, Bencher, Criterion};
use rustc_hash::FxHashMap;
#[cfg(feature = "__rkyv")]
use swc_common::plugin::serialized::{PluginSerializedBytes, VersionedSerializable};
use swc_common::{
Expand Down
2 changes: 1 addition & 1 deletion crates/swc_plugin_runner/tests/css_rkyv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ fn invoke(input: PathBuf) {
tokio::runtime::Runtime::new().unwrap().block_on(async {
// run single plugin
testing::run_test(false, |cm, _handler| {
let fm = cm.new_source_file(FileName::Anon.into(), "console.log(foo)".into());
let fm = cm.load_file(&input).unwrap();

let parsed: Stylesheet =
swc_css_parser::parse_file(&fm, None, Default::default(), &mut Vec::new()).unwrap();
Expand Down
Loading