feat(swc_core): Add macros for wasm bindings (#5430)

This commit is contained in:
OJ Kwon 2022-08-09 20:10:19 -07:00 committed by GitHub
parent 330fbdb8ed
commit 5348195996
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 462 additions and 311 deletions

19
Cargo.lock generated
View File

@ -191,17 +191,23 @@ dependencies = [
[[package]]
name = "binding_core_wasm"
version = "1.2.224"
dependencies = [
"swc_core",
"wasm-bindgen",
]
[[package]]
name = "binding_macros"
version = "0.1.0"
dependencies = [
"anyhow",
"console_error_panic_hook",
"js-sys",
"once_cell",
"parking_lot_core",
"path-clean",
"serde",
"serde_json",
"swc_core",
"tracing",
"swc",
"swc_common",
"swc_ecma_ast",
"swc_ecma_transforms",
"wasm-bindgen",
"wasm-bindgen-futures",
]
@ -3128,6 +3134,7 @@ dependencies = [
name = "swc_core"
version = "0.5.1"
dependencies = [
"binding_macros",
"once_cell",
"swc",
"swc_atoms",

View File

@ -5,6 +5,7 @@ members = [
"crates/jsdoc",
"crates/binding_core_node",
"crates/binding_core_wasm",
"crates/binding_macros",
"crates/swc_cli",
"crates/swc_css",
"crates/swc_css_lints",
@ -34,7 +35,7 @@ members = [
[profile.bench]
debug = true
lto = true
lto = true
# Without this, printing diff consumes more than a minute.

View File

@ -20,26 +20,14 @@ swc_v2 = []
plugin = ["swc_core/plugin_transform_host_js"]
[dependencies]
anyhow = "1.0.58"
console_error_panic_hook = "0.1.7"
js-sys = { version = "0.3.59" }
once_cell = "1.13.0"
parking_lot_core = "0.9.3"
path-clean = "0.1.0"
serde = { version = "1.0.140", features = ["derive"] }
serde_json = "1.0.82"
swc_core = { path = "../swc_core", features = [
"ast",
"common",
"common_perf",
"base",
"binding_macro_wasm",
] }
tracing = { version = "0.1.35", features = ["release_max_level_off"] }
wasm-bindgen = { version = "0.2.82", features = [
"serde-serialize",
"enable-interning",
] }
wasm-bindgen-futures = "0.4.32"
[package.metadata.wasm-pack.profile.release]
wasm-opt = false

View File

@ -1,29 +1,10 @@
#![allow(unused)]
#![deny(warnings)]
#![allow(clippy::unused_unit)]
use std::sync::Arc;
use anyhow::{Context, Error};
use js_sys::{JsString, JSON};
use once_cell::sync::Lazy;
use swc_core::{
ast::{EsVersion, Program},
base::{
config::{ErrorFormat, JsMinifyOptions, Options, ParseOptions, SourceMapsConfig},
try_with_handler, Compiler,
},
common::{comments::Comments, FileName, FilePathMapping, SourceMap},
use swc_core::binding_macros::{
build_minify, build_minify_sync, build_parse, build_parse_sync, build_print, build_print_sync,
build_transform, build_transform_sync,
};
use wasm_bindgen::{prelude::*, JsCast};
use wasm_bindgen_futures::{future_to_promise, spawn_local, JsFuture};
use wasm_bindgen::{prelude::*, JsCast, JsValue};
mod types;
fn convert_err(err: Error, error_format: ErrorFormat) -> JsValue {
error_format.format(&err).into()
}
/// Custom interface definitions for the @swc/wasm's public interface instead of
/// auto generated one, which is not reflecting most of types in detail.
#[wasm_bindgen(typescript_custom_section)]
@ -63,274 +44,14 @@ export function transform(
export function transformSync(code: string | Program, opts?: Options, experimental_plugin_bytes_resolver?: any): Output;
"#;
#[wasm_bindgen(
js_name = "minifySync",
typescript_type = "minifySync",
skip_typescript
)]
pub fn minify_sync(s: JsString, opts: JsValue) -> Result<JsValue, JsValue> {
console_error_panic_hook::set_once();
build_minify_sync!(#[wasm_bindgen(js_name = "minifySync", typescript_type = "minifySync", skip_typescript)]);
build_minify!(#[wasm_bindgen(js_name = "minify", typescript_type = "minify", skip_typescript)]);
let c = compiler();
build_parse_sync!(#[wasm_bindgen(js_name = "parseSync", typescript_type = "parseSync", skip_typescript)]);
build_parse!(#[wasm_bindgen(js_name = "parse", typescript_type = "parse", skip_typescript)]);
try_with_handler(
c.cm.clone(),
swc_core::base::HandlerOpts {
..Default::default()
},
|handler| {
c.run(|| {
let opts = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
opts.into_serde().context("failed to parse options")?
};
build_transform_sync!(#[wasm_bindgen(js_name = "transformSync", typescript_type = "transformSync", skip_typescript)]);
build_transform!(#[wasm_bindgen(js_name = "transform", typescript_type = "transform", skip_typescript)]);
let fm = c.cm.new_source_file(FileName::Anon, s.into());
let program = c
.minify(fm, handler, &opts)
.context("failed to minify file")?;
JsValue::from_serde(&program).context("failed to serialize json")
})
},
)
.map_err(|e| convert_err(e, ErrorFormat::Normal))
}
#[wasm_bindgen(js_name = "minify", typescript_type = "minify", skip_typescript)]
pub fn minify(s: JsString, opts: JsValue) -> js_sys::Promise {
// TODO: This'll be properly scheduled once wasm have standard backed thread
// support.
future_to_promise(async { minify_sync(s, opts) })
}
#[wasm_bindgen(js_name = "parseSync", typescript_type = "parseSync", skip_typescript)]
pub fn parse_sync(s: JsString, opts: JsValue) -> Result<JsValue, JsValue> {
console_error_panic_hook::set_once();
let c = compiler();
try_with_handler(
c.cm.clone(),
swc_core::base::HandlerOpts {
..Default::default()
},
|handler| {
c.run(|| {
let opts: ParseOptions = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
opts.into_serde().context("failed to parse options")?
};
let fm = c.cm.new_source_file(FileName::Anon, s.into());
let cmts = c.comments().clone();
let comments = if opts.comments {
Some(&cmts as &dyn Comments)
} else {
None
};
let program = c
.parse_js(
fm,
handler,
opts.target,
opts.syntax,
opts.is_module,
comments,
)
.context("failed to parse code")?;
JsValue::from_serde(&program).context("failed to serialize json")
})
},
)
.map_err(|e| convert_err(e, ErrorFormat::Normal))
}
#[wasm_bindgen(js_name = "parse", typescript_type = "parse", skip_typescript)]
pub fn parse(s: JsString, opts: JsValue) -> js_sys::Promise {
// TODO: This'll be properly scheduled once wasm have standard backed thread
// support.
future_to_promise(async { parse_sync(s, opts) })
}
#[wasm_bindgen(js_name = "printSync", typescript_type = "printSync", skip_typescript)]
pub fn print_sync(s: JsValue, opts: JsValue) -> Result<JsValue, JsValue> {
console_error_panic_hook::set_once();
let c = compiler();
try_with_handler(
c.cm.clone(),
swc_core::base::HandlerOpts {
..Default::default()
},
|_handler| {
c.run(|| {
let opts: Options = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
opts.into_serde().context("failed to parse options")?
};
let program: Program = s.into_serde().context("failed to deserialize program")?;
let s = c
.print(
&program,
None,
None,
true,
opts.codegen_target().unwrap_or(EsVersion::Es2020),
opts.source_maps
.clone()
.unwrap_or(SourceMapsConfig::Bool(false)),
&Default::default(),
None,
opts.config.minify.into(),
None,
opts.config.emit_source_map_columns.into_bool(),
false,
)
.context("failed to print code")?;
JsValue::from_serde(&s).context("failed to serialize json")
})
},
)
.map_err(|e| convert_err(e, ErrorFormat::Normal))
}
#[wasm_bindgen(js_name = "print", typescript_type = "print", skip_typescript)]
pub fn print(s: JsValue, opts: JsValue) -> js_sys::Promise {
// TODO: This'll be properly scheduled once wasm have standard backed thread
// support.
future_to_promise(async { print_sync(s, opts) })
}
#[wasm_bindgen(
js_name = "transformSync",
typescript_type = "transformSync",
skip_typescript
)]
#[allow(unused_variables)]
pub fn transform_sync(
s: JsValue,
opts: JsValue,
experimental_plugin_bytes_resolver: JsValue,
) -> Result<JsValue, JsValue> {
console_error_panic_hook::set_once();
let c = compiler();
#[cfg(feature = "plugin")]
{
if experimental_plugin_bytes_resolver.is_object() {
use js_sys::{Array, Object, Uint8Array};
use wasm_bindgen::JsCast;
// TODO: This is probably very inefficient, including each transform
// deserializes plugin bytes.
let plugin_bytes_resolver_object: Object = experimental_plugin_bytes_resolver
.try_into()
.expect("Resolver should be a js object");
swc_core::plugin_runner::cache::init_plugin_module_cache_once();
let entries = Object::entries(&plugin_bytes_resolver_object);
for entry in entries.iter() {
let entry: Array = entry
.try_into()
.expect("Resolver object missing either key or value");
let name: String = entry
.get(0)
.as_string()
.expect("Resolver key should be a string");
let buffer = entry.get(1);
//https://github.com/rustwasm/wasm-bindgen/issues/2017#issue-573013044
//We may use https://github.com/cloudflare/serde-wasm-bindgen instead later
let data = if JsCast::is_instance_of::<Uint8Array>(&buffer) {
JsValue::from(Array::from(&buffer))
} else {
buffer
};
let bytes: Vec<u8> = data
.into_serde()
.expect("Could not read byte from plugin resolver");
// In here we 'inject' externally loaded bytes into the cache, so
// remaining plugin_runner execution path works as much as
// similar between embedded runtime.
swc_core::plugin_runner::cache::PLUGIN_MODULE_CACHE.store_once(&name, bytes);
}
}
}
let opts: Options = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
opts.into_serde()
.context("failed to parse options")
.map_err(|e| convert_err(e, ErrorFormat::Normal))?
};
let error_format = opts.experimental.error_format.unwrap_or_default();
try_with_handler(
c.cm.clone(),
swc_core::base::HandlerOpts {
..Default::default()
},
|handler| {
c.run(|| {
let s = s.dyn_into::<js_sys::JsString>();
let out = match s {
Ok(s) => {
let fm = c.cm.new_source_file(
if opts.filename.is_empty() {
FileName::Anon
} else {
FileName::Real(opts.filename.clone().into())
},
s.into(),
);
c.process_js_file(fm, handler, &opts)
.context("failed to process input file")?
}
Err(v) => unsafe { c.process_js(handler, v.into_serde().expect(""), &opts)? },
};
JsValue::from_serde(&out).context("failed to serialize json")
})
},
)
.map_err(|e| convert_err(e, error_format))
}
#[wasm_bindgen(js_name = "transform", typescript_type = "transform", skip_typescript)]
pub fn transform(
s: JsValue,
opts: JsValue,
experimental_plugin_bytes_resolver: JsValue,
) -> js_sys::Promise {
// TODO: This'll be properly scheduled once wasm have standard backed thread
// support.
future_to_promise(async { transform_sync(s, opts, experimental_plugin_bytes_resolver) })
}
/// Get global sourcemap
fn compiler() -> Arc<Compiler> {
static C: Lazy<Arc<Compiler>> = Lazy::new(|| {
let cm = Arc::new(SourceMap::new(FilePathMapping::empty()));
Arc::new(Compiler::new(cm))
});
C.clone()
}
build_print_sync!(#[wasm_bindgen(js_name = "printSync", typescript_type = "printSync", skip_typescript)]);
build_print!(#[wasm_bindgen(js_name = "print", typescript_type = "print", skip_typescript)]);

View File

@ -0,0 +1,47 @@
[package]
authors = ["강동윤 <kdy1997.dev@gmail.com>", "OJ Kwon <kwon.ohjoong@gmail.com>"]
description = "Macros to build customized bindings interface"
edition = "2021"
license = "Apache-2.0"
name = "binding_macros"
repository = "https://github.com/swc-project/swc.git"
version = "0.1.0"
[lib]
bench = false
[features]
binding_native = []
binding_wasm = [
# SWC features
"swc",
"swc_common",
"swc_ecma_transforms",
"swc_ecma_ast",
# Optional packages
"once_cell",
"wasm-bindgen",
"wasm-bindgen-futures",
"js-sys",
"anyhow",
"console_error_panic_hook",
]
[dependencies]
# Common deps for the SWC imports
swc = { optional = true, version = "0.213.0", path = "../swc" }
swc_common = { optional = true, version = "0.27.0", path = "../swc_common" }
swc_ecma_ast = { optional = true, version = "0.90.0", path = "../swc_ecma_ast" }
swc_ecma_transforms = { optional = true, version = "0.182.0", path = "../swc_ecma_transforms" }
# Optional deps for the wasm binding macro
anyhow = { optional = true, version = "1.0.58" }
console_error_panic_hook = { optional = true, version = "0.1.7" }
js-sys = { optional = true, version = "0.3.59" }
once_cell = { optional = true, version = "1.13.0" }
wasm-bindgen = { optional = true, version = "0.2.82", features = [
"serde-serialize",
"enable-interning",
] }
wasm-bindgen-futures = { optional = true, version = "0.4.32" }

View File

@ -0,0 +1,8 @@
#[cfg(all(feature = "binding_node", feature = "binding_wasm",))]
compile_error!(
"Macro cannot enable multiple bindings builder at once. Enable only one binding feature"
);
fn main() {
//noop
}

View File

@ -0,0 +1,2 @@
#[cfg(feature = "binding_wasm")]
pub mod wasm;

View File

@ -0,0 +1,366 @@
use std::sync::Arc;
#[doc(hidden)]
pub use anyhow;
use anyhow::Error;
#[doc(hidden)]
pub use js_sys;
use once_cell::sync::Lazy;
use swc::{config::ErrorFormat, Compiler};
#[doc(hidden)]
pub use swc::{
config::{Options, ParseOptions, SourceMapsConfig},
try_with_handler,
};
#[doc(hidden)]
pub use swc_common::{comments, FileName};
use swc_common::{FilePathMapping, SourceMap};
#[doc(hidden)]
pub use swc_ecma_ast::{EsVersion, Program};
#[doc(hidden)]
pub use swc_ecma_transforms::pass::noop;
#[doc(hidden)]
pub use wasm_bindgen::JsValue;
#[doc(hidden)]
pub use wasm_bindgen_futures::future_to_promise;
/// Get global sourcemap
pub fn compiler() -> Arc<Compiler> {
console_error_panic_hook::set_once();
static C: Lazy<Arc<Compiler>> = Lazy::new(|| {
let cm = Arc::new(SourceMap::new(FilePathMapping::empty()));
Arc::new(Compiler::new(cm))
});
C.clone()
}
#[doc(hidden)]
pub fn convert_err(
err: Error,
error_format: Option<ErrorFormat>,
) -> wasm_bindgen::prelude::JsValue {
error_format
.unwrap_or(ErrorFormat::Normal)
.format(&err)
.into()
}
#[macro_export]
macro_rules! build_minify_sync {
($(#[$m:meta])*) => {
build_minify_sync!($(#[$m])*, Default::default());
};
($(#[$m:meta])*, $opt: expr) => {
$(#[$m])*
pub fn minify_sync(s: $crate::wasm::js_sys::JsString, opts: $crate::wasm::JsValue) -> Result<$crate::wasm::JsValue, $crate::wasm::JsValue> {
let c = $crate::wasm::compiler();
$crate::wasm::try_with_handler(
c.cm.clone(),
$opt,
|handler| {
c.run(|| {
let opts = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
$crate::wasm::anyhow::Context::context(opts.into_serde(), "failed to parse options")?
};
let fm = c.cm.new_source_file($crate::wasm::FileName::Anon, s.into());
let program = $crate::wasm::anyhow::Context::context(c.minify(fm, handler, &opts), "failed to minify file")?;
$crate::wasm::anyhow::Context::context($crate::wasm::JsValue::from_serde(&program), "failed to serialize json")
})
},
)
.map_err(|e| $crate::wasm::convert_err(e, None))
}
};
}
/// Currently this relies on existence of minify_sync.
#[macro_export]
macro_rules! build_minify {
($(#[$m:meta])*) => {
build_minify!($(#[$m])*, Default::default());
};
($(#[$m:meta])*, $opt: expr) => {
$(#[$m])*
pub fn minify(s: $crate::wasm::js_sys::JsString, opts: $crate::wasm::JsValue) -> $crate::wasm::js_sys::Promise {
// TODO: This'll be properly scheduled once wasm have standard backed thread
// support.
$crate::wasm::future_to_promise(async { minify_sync(s, opts) })
}
};
}
#[macro_export]
macro_rules! build_parse_sync {
($(#[$m:meta])*) => {
build_parse_sync!($(#[$m])*, Default::default());
};
($(#[$m:meta])*, $opt: expr) => {
$(#[$m])*
pub fn parse_sync(s: $crate::wasm::js_sys::JsString, opts: $crate::wasm::JsValue) -> Result<$crate::wasm::JsValue, $crate::wasm::JsValue> {
let c = $crate::wasm::compiler();
$crate::wasm::try_with_handler(
c.cm.clone(),
$opt,
|handler| {
c.run(|| {
let opts: $crate::wasm::ParseOptions = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
$crate::wasm::anyhow::Context::context(opts.into_serde(), "failed to parse options")?
};
let fm = c.cm.new_source_file($crate::wasm::FileName::Anon, s.into());
let cmts = c.comments().clone();
let comments = if opts.comments {
Some(&cmts as &dyn $crate::wasm::comments::Comments)
} else {
None
};
let program = $crate::wasm::anyhow::Context::context(
c
.parse_js(
fm,
handler,
opts.target,
opts.syntax,
opts.is_module,
comments,
),
"failed to parse code"
)?;
$crate::wasm::anyhow::Context::context($crate::wasm::JsValue::from_serde(&program), "failed to serialize json")
})
},
)
.map_err(|e| $crate::wasm::convert_err(e, None))
}
};
}
/// Currently this relies on existence of parse_sync.
#[macro_export]
macro_rules! build_parse {
($(#[$m:meta])*) => {
build_parse!($(#[$m])*, Default::default());
};
($(#[$m:meta])*, $opt: expr) => {
$(#[$m])*
pub fn parse(s: $crate::wasm::js_sys::JsString, opts: $crate::wasm::JsValue) -> $crate::wasm::js_sys::Promise {
// TODO: This'll be properly scheduled once wasm have standard backed thread
// support.
$crate::wasm::future_to_promise(async { parse_sync(s, opts) })
}
};
}
#[macro_export]
macro_rules! build_print_sync {
($(#[$m:meta])*) => {
build_print_sync!($(#[$m])*, Default::default());
};
($(#[$m:meta])*, $opt: expr) => {
$(#[$m])*
pub fn print_sync(s: $crate::wasm::JsValue, opts: $crate::wasm::JsValue) -> Result<$crate::wasm::JsValue, $crate::wasm::JsValue> {
let c = $crate::wasm::compiler();
$crate::wasm::try_with_handler(
c.cm.clone(),
$opt,
|_handler| {
c.run(|| {
let opts: $crate::wasm::Options = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
$crate::wasm::anyhow::Context::context(opts.into_serde(), "failed to parse options")?
};
let program: $crate::wasm::Program = $crate::wasm::anyhow::Context::context(s.into_serde(), "failed to deserialize program")?;
let s = $crate::wasm::anyhow::Context::context(c
.print(
&program,
None,
None,
true,
opts.codegen_target().unwrap_or($crate::wasm::EsVersion::Es2020),
opts.source_maps
.clone()
.unwrap_or($crate::wasm::SourceMapsConfig::Bool(false)),
&Default::default(),
None,
opts.config.minify.into(),
None,
opts.config.emit_source_map_columns.into_bool(),
false,
),"failed to print code")?;
$crate::wasm::anyhow::Context::context(JsValue::from_serde(&s), "failed to serialize json")
})
},
)
.map_err(|e| $crate::wasm::convert_err(e, None))
}
};
}
/// Currently this relies on existence of print_sync.
#[macro_export]
macro_rules! build_print {
($(#[$m:meta])*) => {
build_print!($(#[$m])*, Default::default());
};
($(#[$m:meta])*, $opt: expr) => {
$(#[$m])*
pub fn print(s: $crate::wasm::JsValue, opts: $crate::wasm::JsValue) -> $crate::wasm::js_sys::Promise {
// TODO: This'll be properly scheduled once wasm have standard backed thread
// support.
$crate::wasm::future_to_promise(async { print_sync(s, opts) })
}
};
}
#[macro_export]
macro_rules! build_transform_sync {
($(#[$m:meta])*) => {
build_transform_sync!($(#[$m])*, |_, _| $crate::wasm::noop(), |_, _| $crate::wasm::noop(), Default::default());
};
($(#[$m:meta])*, $before_pass: expr, $after_pass: expr) => {
build_transform_sync!($(#[$m])*, $before_pass, $after_pass, Default::default());
};
($(#[$m:meta])*, $before_pass: expr, $after_pass: expr, $opt: expr) => {
$(#[$m])*
#[allow(unused_variables)]
pub fn transform_sync(
s: $crate::wasm::JsValue,
opts: $crate::wasm::JsValue,
experimental_plugin_bytes_resolver: $crate::wasm::JsValue,
) -> Result<$crate::wasm::JsValue, $crate::wasm::JsValue> {
let c = $crate::wasm::compiler();
#[cfg(feature = "plugin")]
{
if experimental_plugin_bytes_resolver.is_object() {
use $crate::wasm::js_sys::{Array, Object, Uint8Array};
// TODO: This is probably very inefficient, including each transform
// deserializes plugin bytes.
let plugin_bytes_resolver_object: Object = experimental_plugin_bytes_resolver
.try_into()
.expect("Resolver should be a js object");
swc_core::plugin_runner::cache::init_plugin_module_cache_once();
let entries = Object::entries(&plugin_bytes_resolver_object);
for entry in entries.iter() {
let entry: Array = entry
.try_into()
.expect("Resolver object missing either key or value");
let name: String = entry
.get(0)
.as_string()
.expect("Resolver key should be a string");
let buffer = entry.get(1);
//https://github.com/rustwasm/wasm-bindgen/issues/2017#issue-573013044
//We may use https://github.com/cloudflare/serde-wasm-bindgen instead later
let data = if JsCast::is_instance_of::<Uint8Array>(&buffer) {
JsValue::from(Array::from(&buffer))
} else {
buffer
};
let bytes: Vec<u8> = data
.into_serde()
.expect("Could not read byte from plugin resolver");
// In here we 'inject' externally loaded bytes into the cache, so
// remaining plugin_runner execution path works as much as
// similar between embedded runtime.
swc_core::plugin_runner::cache::PLUGIN_MODULE_CACHE.store_once(&name, bytes);
}
}
}
let opts: $crate::wasm::Options = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
$crate::wasm::anyhow::Context::context(opts.into_serde(), "failed to parse options")
.map_err(|e| $crate::wasm::convert_err(e, None))?
};
let error_format = opts.experimental.error_format.unwrap_or_default();
$crate::wasm::try_with_handler(
c.cm.clone(),
$opt,
|handler| {
c.run(|| {
let s = JsCast::dyn_into::<$crate::wasm::js_sys::JsString>(s);
let out = match s {
Ok(s) => {
let fm = c.cm.new_source_file(
if opts.filename.is_empty() {
$crate::wasm::FileName::Anon
} else {
$crate::wasm::FileName::Real(opts.filename.clone().into())
},
s.into(),
);
let cm = c.cm.clone();
let file = fm.clone();
$crate::wasm::anyhow::Context::context(
c.process_js_with_custom_pass(
fm,
None,
handler,
&opts,
$before_pass,
$after_pass,
), "failed to process js file"
)?
}
Err(v) => unsafe { c.process_js(handler, v.into_serde().expect(""), &opts)? },
};
$crate::wasm::anyhow::Context::context($crate::wasm::JsValue::from_serde(&out),
"failed to serialize json")
})
},
)
.map_err(|e| $crate::wasm::convert_err(e, Some(error_format)))
}
};
}
/// Currently this relies on existence of transform_sync.
#[macro_export]
macro_rules! build_transform {
($(#[$m:meta])*) => {
build_transform!($(#[$m])*, Default::default());
};
($(#[$m:meta])*, $opt: expr) => {
$(#[$m])*
pub fn transform(
s: $crate::wasm::JsValue,
opts: $crate::wasm::JsValue,
experimental_plugin_bytes_resolver: $crate::wasm::JsValue,
) -> $crate::wasm::js_sys::Promise {
// TODO: This'll be properly scheduled once wasm have standard backed thread
// support.
$crate::wasm::future_to_promise(async { transform_sync(s, opts, experimental_plugin_bytes_resolver) })
}
};
}

View File

@ -103,6 +103,9 @@ ast = ["swc_ecma_ast", "swc_atoms"]
# we may encapsulate `tracing` package into swc_core.
trace_macro = ["swc_trace_macro"]
binding_macro_native = ["__binding_macros", "binding_macros/binding_native"]
binding_macro_wasm = ["__binding_macros", "binding_macros/binding_wasm"]
## Plugins
# Top level features should be enabled to write plugins for the custom transform.
plugin_transform = [
@ -203,6 +206,7 @@ __plugin_transform_schema_test = [
## Common
__base = ["swc"]
__binding_macros = ["common", "__base", "__transforms", "ast", "binding_macros"]
__common = ["swc_common"]
__testing_transform = ["swc_ecma_transforms_testing"]
__transforms = ["swc_ecma_transforms"]
@ -215,6 +219,7 @@ wasmer = { optional = true, version = "2.3.0", default-features = false }
wasmer-wasi = { optional = true, version = "2.3.0", default-features = false }
# swc_* dependencies
binding_macros = { optional = true, version = "0.1.0", path = "../binding_macros" }
swc = { optional = true, version = "0.213.0", path = "../swc" }
swc_atoms = { optional = true, version = "0.4.0", path = "../swc_atoms" }
swc_bundler = { optional = true, version = "0.176.0", path = "../swc_bundler" }

View File

@ -99,6 +99,12 @@ pub mod testing_transform {
pub use swc_ecma_transforms_testing::*;
}
#[cfg(any(docsrs, feature = "__binding_macros"))]
#[cfg_attr(docsrs, doc(cfg(feature = "__binding_macros")))]
pub mod binding_macros {
pub use binding_macros::*;
}
#[cfg(any(docsrs, feature = "allocator_node"))]
#[cfg_attr(docsrs, doc(cfg(feature = "allocator_node")))]
extern crate swc_node_base;