Skip to content

Commit d213a84

Browse files
authored
feat(es): Add Rust plugin host part for analysis API (#10285)
1 parent 64fb286 commit d213a84

File tree

11 files changed

+162
-12
lines changed

11 files changed

+162
-12
lines changed

.changeset/moody-houses-judge.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
swc_core: minor
3+
swc: minor
4+
---
5+
6+
feat(es): Add Rust plugin host part for analysis API

.vscode/settings.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,8 @@
2323
"crates/swc_ecma_transforms_proposal/tests/decorator-tests"
2424
],
2525
"eslint.enable": false,
26-
"rust-analyzer.check.command": "clippy"
27-
}
26+
"rust-analyzer.check.command": "clippy",
27+
"rust-analyzer.cargo.features": [
28+
"plugin"
29+
]
30+
}

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/swc/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ jsonc-parser = { workspace = true, features = ["serde"] }
6060
lru = { workspace = true }
6161
once_cell = { workspace = true }
6262
par-core = { workspace = true }
63+
par-iter = { workspace = true }
6364
parking_lot = { workspace = true }
6465
pathdiff = { workspace = true }
6566
regex = { workspace = true }
@@ -70,6 +71,7 @@ sourcemap = { workspace = true }
7071
tracing = { workspace = true }
7172
url = { workspace = true }
7273

74+
7375
swc_atoms = { version = "5.0.0", path = "../swc_atoms" }
7476
swc_cached = { version = "2.0.0", path = "../swc_cached" }
7577
swc_common = { version = "8.0.1", path = "../swc_common", features = [
@@ -133,11 +135,12 @@ ansi_term = { workspace = true }
133135
criterion = { workspace = true }
134136
flate2 = { workspace = true }
135137
humansize = { workspace = true }
136-
rayon = { workspace = true }
137138
walkdir = { workspace = true }
138139

139140

140141
codspeed-criterion-compat = { workspace = true }
142+
par-core = { workspace = true, features = ["chili"] }
143+
141144
swc_ecma_ast = { version = "8.1.0", path = "../swc_ecma_ast", features = [
142145
"serde-impl",
143146
] }

crates/swc/src/config/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -624,10 +624,10 @@ impl Options {
624624
for plugin_config in plugins.iter() {
625625
let plugin_name = &plugin_config.0;
626626

627-
if !inner_cache.contains(&plugin_name) {
627+
if !inner_cache.contains(plugin_name) {
628628
let resolved_path = plugin_resolver.resolve(
629-
&FileName::Real(PathBuf::from(&plugin_name)),
630-
&plugin_name,
629+
&FileName::Real(PathBuf::from(plugin_name)),
630+
plugin_name,
631631
)?;
632632

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

639-
inner_cache.store_bytes_from_path(&path, &plugin_name)?;
639+
inner_cache.store_bytes_from_path(&path, plugin_name)?;
640640
tracing::debug!("Initialized WASM plugin {plugin_name}");
641641
}
642642
}
@@ -1731,7 +1731,7 @@ impl GlobalPassOption {
17311731
}
17321732
}
17331733

1734-
fn default_env_name() -> String {
1734+
pub(crate) fn default_env_name() -> String {
17351735
if let Ok(v) = env::var("SWC_ENV") {
17361736
return v;
17371737
}

crates/swc/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ mod builder;
170170
pub mod config;
171171
mod dropped_comments_preserver;
172172
mod plugin;
173+
mod wasm_analysis;
173174
pub mod resolver {
174175
use std::path::PathBuf;
175176

crates/swc/src/plugin.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ impl RustPlugins {
115115
.unwrap()
116116
.lock()
117117
.get_fs_cache_root()
118-
.map(|v| std::path::PathBuf::from(v)),
118+
.map(std::path::PathBuf::from),
119119
);
120120
let mut transform_plugin_executor =
121121
swc_plugin_runner::create_plugin_transform_executor(

crates/swc/src/wasm_analysis.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#![cfg(feature = "plugin")]
2+
3+
use std::sync::Arc;
4+
5+
use anyhow::{Context, Result};
6+
use common::{
7+
comments::SingleThreadedComments,
8+
errors::Handler,
9+
plugin::{metadata::TransformPluginMetadataContext, serialized::PluginSerializedBytes},
10+
Mark, SourceFile, GLOBALS,
11+
};
12+
use par_iter::iter::{IntoParallelRefIterator, ParallelIterator};
13+
use serde::Deserialize;
14+
use swc_config::IsModule;
15+
use swc_ecma_ast::EsVersion;
16+
use swc_ecma_parser::Syntax;
17+
use swc_ecma_transforms::resolver;
18+
19+
use crate::{plugin::PluginConfig, Compiler};
20+
21+
impl Compiler {
22+
/// Run analysis using Wasm plugins.
23+
pub fn run_wasm_analysis(
24+
&self,
25+
fm: Arc<SourceFile>,
26+
handler: &Handler,
27+
opts: &WasmAnalysisOptions,
28+
comments: SingleThreadedComments,
29+
) -> Result<String> {
30+
self.run(|| {
31+
GLOBALS.with(|globals| {
32+
let unresolved_mark = Mark::new();
33+
let top_level_mark = Mark::new();
34+
35+
let mut program = self.parse_js(
36+
fm.clone(),
37+
handler,
38+
EsVersion::latest(),
39+
opts.parser,
40+
opts.module,
41+
Some(&comments),
42+
)?;
43+
44+
program.mutate(resolver(
45+
unresolved_mark,
46+
top_level_mark,
47+
opts.parser.typescript(),
48+
));
49+
50+
let serialized = {
51+
let _span = tracing::span!(tracing::Level::INFO, "serialize_program").entered();
52+
let program =
53+
swc_common::plugin::serialized::VersionedSerializable::new(program);
54+
PluginSerializedBytes::try_serialize(&program)?
55+
};
56+
57+
let transform_metadata_context = Arc::new(TransformPluginMetadataContext::new(
58+
Some(fm.name.to_string()),
59+
crate::config::default_env_name(),
60+
None,
61+
));
62+
63+
let result = opts
64+
.plugins
65+
.par_iter()
66+
.map(|p| {
67+
GLOBALS.set(globals, || {
68+
let plugin_module_bytes = crate::config::PLUGIN_MODULE_CACHE
69+
.inner
70+
.get()
71+
.unwrap()
72+
.lock()
73+
.get(&p.0)
74+
.expect("plugin module should be loaded");
75+
76+
let plugin_name = plugin_module_bytes.get_module_name().to_string();
77+
let runtime = swc_plugin_runner::wasix_runtime::build_wasi_runtime(
78+
crate::config::PLUGIN_MODULE_CACHE
79+
.inner
80+
.get()
81+
.unwrap()
82+
.lock()
83+
.get_fs_cache_root()
84+
.map(std::path::PathBuf::from),
85+
);
86+
let mut transform_plugin_executor =
87+
swc_plugin_runner::create_plugin_transform_executor(
88+
&self.cm,
89+
&unresolved_mark,
90+
&transform_metadata_context,
91+
plugin_module_bytes,
92+
Some(p.1.clone()),
93+
runtime,
94+
);
95+
96+
let span = tracing::span!(
97+
tracing::Level::INFO,
98+
"execute_plugin_runner",
99+
plugin_module = p.0.as_str()
100+
)
101+
.entered();
102+
103+
let (result, output) = swc_transform_common::output::capture(|| {
104+
transform_plugin_executor
105+
.transform(&serialized, Some(true))
106+
.with_context(|| {
107+
format!(
108+
"failed to invoke `{}` as js analysis plugin at {}",
109+
&p.0, plugin_name
110+
)
111+
})
112+
});
113+
result?;
114+
drop(span);
115+
116+
Ok(output)
117+
})
118+
})
119+
.collect::<Result<Vec<_>>>()?;
120+
121+
serde_json::to_string(&result)
122+
.map_err(|e| anyhow::anyhow!("Failed to serialize output: {e}"))
123+
})
124+
})
125+
}
126+
}
127+
128+
#[derive(Debug, Deserialize)]
129+
pub struct WasmAnalysisOptions {
130+
#[serde(default)]
131+
pub parser: Syntax,
132+
#[serde(default)]
133+
pub module: IsModule,
134+
135+
pub plugins: Vec<PluginConfig>,
136+
}

crates/swc/tests/projects.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{
55
};
66

77
use anyhow::Context;
8-
use rayon::prelude::*;
8+
use par_iter::prelude::*;
99
use swc::{
1010
config::{
1111
Config, FileMatcher, JsMinifyOptions, JscConfig, ModuleConfig, Options, Paths,

crates/swc_plugin_runner/benches/ecma_invoke.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::{
1010
};
1111

1212
use codspeed_criterion_compat::{black_box, criterion_group, criterion_main, Bencher, Criterion};
13+
use rustc_hash::FxHashMap;
1314
#[cfg(feature = "__rkyv")]
1415
use swc_common::plugin::serialized::{PluginSerializedBytes, VersionedSerializable};
1516
use swc_common::{

0 commit comments

Comments
 (0)