mirror of
https://github.com/swc-project/swc.git
synced 2024-10-04 04:07:18 +03:00
feat(swc_core): Add macros for wasm bindings (#5430)
This commit is contained in:
parent
330fbdb8ed
commit
5348195996
19
Cargo.lock
generated
19
Cargo.lock
generated
@ -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",
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)]);
|
||||
|
47
crates/binding_macros/Cargo.toml
Normal file
47
crates/binding_macros/Cargo.toml
Normal 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" }
|
8
crates/binding_macros/build.rs
Normal file
8
crates/binding_macros/build.rs
Normal 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
|
||||
}
|
2
crates/binding_macros/src/lib.rs
Normal file
2
crates/binding_macros/src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
||||
#[cfg(feature = "binding_wasm")]
|
||||
pub mod wasm;
|
366
crates/binding_macros/src/wasm.rs
Normal file
366
crates/binding_macros/src/wasm.rs
Normal 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) })
|
||||
}
|
||||
};
|
||||
}
|
@ -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" }
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user