From dd4b9e87de93294ed402c357745a2e0d268b34ef Mon Sep 17 00:00:00 2001 From: OJ Kwon <1210596+kwonoj@users.noreply.github.com> Date: Thu, 17 Nov 2022 17:21:23 -0800 Subject: [PATCH] refactor(bindings): Deprecate `jsvalue::*_serde` (#6462) --- .github/workflows/CI.yml | 1 + Cargo.lock | 32 ++--- bindings/binding_core_wasm/Cargo.toml | 1 - bindings/binding_core_wasm/src/lib.rs | 48 ++++++-- crates/binding_macros/Cargo.toml | 7 +- crates/binding_macros/src/wasm.rs | 115 +++++++++++++----- crates/swc_core/.gitignore | 2 + crates/swc_core/Cargo.toml | 3 + .../tests/fixture/stub_napi/Cargo.toml | 62 ++++++++++ .../swc_core/tests/fixture/stub_napi/build.rs | 29 +++++ .../tests/fixture/stub_napi/src/lib.rs | 52 ++++++++ .../tests/fixture/stub_wasm/Cargo.toml | 29 +++++ .../tests/fixture/stub_wasm/src/lib.rs | 14 +++ crates/swc_core/tests/integration.rs | 51 ++++++++ 14 files changed, 388 insertions(+), 58 deletions(-) create mode 100644 crates/swc_core/.gitignore create mode 100644 crates/swc_core/tests/fixture/stub_napi/Cargo.toml create mode 100644 crates/swc_core/tests/fixture/stub_napi/build.rs create mode 100644 crates/swc_core/tests/fixture/stub_napi/src/lib.rs create mode 100644 crates/swc_core/tests/fixture/stub_wasm/Cargo.toml create mode 100644 crates/swc_core/tests/fixture/stub_wasm/src/lib.rs create mode 100644 crates/swc_core/tests/integration.rs diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 45e55a689ab3..ba363633fb51 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -641,6 +641,7 @@ jobs: - name: Run cargo test (core) if: matrix.settings.crate == 'swc_core' run: | + rustup target add wasm32-unknown-unknown cargo test -p swc_core --features ecma_quote --features common --features ecma_utils - name: Run cargo test (binding_core_wasm) diff --git a/Cargo.lock b/Cargo.lock index 28940c246c20..0ecf515d3361 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,9 +60,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.62" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305" +checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" [[package]] name = "arbitrary" @@ -174,10 +174,13 @@ dependencies = [ "console_error_panic_hook", "js-sys", "once_cell", + "serde", + "serde-wasm-bindgen", "swc", "swc_common", "swc_ecma_ast", "swc_ecma_transforms", + "swc_ecma_visit", "wasm-bindgen", "wasm-bindgen-futures", ] @@ -2655,9 +2658,9 @@ dependencies = [ [[package]] name = "serde-wasm-bindgen" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfc62771e7b829b517cb213419236475f434fb480eddd76112ae182d274434a" +checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" dependencies = [ "js-sys", "serde", @@ -3164,6 +3167,7 @@ dependencies = [ name = "swc_core" version = "0.43.18" dependencies = [ + "anyhow", "binding_macros", "once_cell", "swc", @@ -4915,9 +4919,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if 1.0.0", "serde", @@ -4927,9 +4931,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", "log", @@ -4954,9 +4958,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4964,9 +4968,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", @@ -4977,9 +4981,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "wasmer" diff --git a/bindings/binding_core_wasm/Cargo.toml b/bindings/binding_core_wasm/Cargo.toml index 672c18e0e5a2..c12d29a372ad 100644 --- a/bindings/binding_core_wasm/Cargo.toml +++ b/bindings/binding_core_wasm/Cargo.toml @@ -31,7 +31,6 @@ swc_core = { version = "0.43.15", features = [ ] } tracing = { version = "0.1.37", features = ["max_level_off"] } wasm-bindgen = { version = "0.2.82", features = [ - "serde-serialize", "enable-interning", ] } diff --git a/bindings/binding_core_wasm/src/lib.rs b/bindings/binding_core_wasm/src/lib.rs index 9df28d760676..0ecf9f981e46 100644 --- a/bindings/binding_core_wasm/src/lib.rs +++ b/bindings/binding_core_wasm/src/lib.rs @@ -1,4 +1,6 @@ use anyhow::Error; +use serde::Serialize; +use serde_wasm_bindgen::Serializer; use swc_core::{ base::HandlerOpts, binding_macros::wasm::{ @@ -21,6 +23,12 @@ use swc_core::{ use wasm_bindgen::{prelude::*, JsCast}; mod types; +// A serializer with options to provide backward compat for the input / output +// from the bindgen generated swc interfaces. +const COMPAT_SERIALIZER: Serializer = Serializer::new() + .serialize_maps_as_objects(true) + .serialize_missing_as_null(true); + /// 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)] @@ -81,12 +89,16 @@ pub fn minify_sync(s: JsString, opts: JsValue) -> Result { let opts = if opts.is_null() || opts.is_undefined() { Default::default() } else { - anyhow::Context::context(opts.into_serde(), "failed to parse options")? + serde_wasm_bindgen::from_value(opts) + .map_err(|e| anyhow::anyhow!("failed to parse options: {}", e))? }; let fm = c.cm.new_source_file(FileName::Anon, s.into()); let program = anyhow::Context::context(c.minify(fm, handler, &opts), "failed to minify file")?; - anyhow::Context::context(JsValue::from_serde(&program), "failed to serialize json") + + program + .serialize(&COMPAT_SERIALIZER) + .map_err(|e| anyhow::anyhow!("failed to serialize program: {}", e)) }) }) .map_err(|e| convert_err(e, None)) @@ -106,7 +118,8 @@ pub fn parse_sync(s: JsString, opts: JsValue) -> Result { let opts: ParseOptions = if opts.is_null() || opts.is_undefined() { Default::default() } else { - anyhow::Context::context(opts.into_serde(), "failed to parse options")? + serde_wasm_bindgen::from_value(opts) + .map_err(|e| anyhow::anyhow!("failed to parse options: {}", e))? }; let fm = c.cm.new_source_file(FileName::Anon, s.into()); let cmts = c.comments().clone(); @@ -133,7 +146,9 @@ pub fn parse_sync(s: JsString, opts: JsValue) -> Result { opts.syntax.typescript(), )); - anyhow::Context::context(JsValue::from_serde(&program), "failed to serialize json") + program + .serialize(&COMPAT_SERIALIZER) + .map_err(|e| anyhow::anyhow!("failed to serialize program: {}", e)) }) }) }) @@ -160,9 +175,9 @@ pub fn transform_sync( let opts: Options = if opts.is_null() || opts.is_undefined() { Default::default() } else { - anyhow::Context::context(opts.into_serde(), "failed to parse options") - .map_err(|e| convert_err(e, None))? + serde_wasm_bindgen::from_value(opts)? }; + let error_format = opts.experimental.error_format.unwrap_or_default(); try_with_handler(c.cm.clone(), Default::default(), |handler| { c.run(|| { @@ -193,9 +208,13 @@ pub fn transform_sync( "failed to process js file", )? } - Err(v) => unsafe { c.process_js(handler, v.into_serde().expect(""), &opts)? }, + Err(v) => { + c.process_js(handler, serde_wasm_bindgen::from_value(v).expect("Should able to deserialize into program"), &opts)? + } }; - anyhow::Context::context(JsValue::from_serde(&out), "failed to serialize json") + + out.serialize(&COMPAT_SERIALIZER) + .map_err(|e| anyhow::anyhow!("failed to serialize transform result: {}", e)) }) }) .map_err(|e| convert_err(e, Some(error_format))) @@ -218,10 +237,13 @@ pub fn print_sync(s: JsValue, opts: JsValue) -> Result { let opts: Options = if opts.is_null() || opts.is_undefined() { Default::default() } else { - anyhow::Context::context(opts.into_serde(), "failed to parse options")? + serde_wasm_bindgen::from_value(opts) + .map_err(|e| anyhow::anyhow!("failed to parse options: {}", e))? }; - let program: Program = - anyhow::Context::context(s.into_serde(), "failed to deserialize program")?; + + let program: Program = serde_wasm_bindgen::from_value(s) + .map_err(|e| anyhow::anyhow!("failed to deserialize program: {}", e))?; + let s = anyhow::Context::context( c.print( &program, @@ -241,7 +263,9 @@ pub fn print_sync(s: JsValue, opts: JsValue) -> Result { ), "failed to print code", )?; - anyhow::Context::context(JsValue::from_serde(&s), "failed to serialize json") + + serde_wasm_bindgen::to_value(&s) + .map_err(|e| anyhow::anyhow!("failed to serialize json: {}", e)) }) }) .map_err(|e| convert_err(e, None)) diff --git a/crates/binding_macros/Cargo.toml b/crates/binding_macros/Cargo.toml index 043c5a9510c7..2d90d89be01e 100644 --- a/crates/binding_macros/Cargo.toml +++ b/crates/binding_macros/Cargo.toml @@ -18,12 +18,15 @@ binding_wasm = [ "swc_common", "swc_ecma_transforms", "swc_ecma_ast", + "swc_ecma_visit", # Optional packages "once_cell", "wasm-bindgen", "wasm-bindgen-futures", "js-sys", + "serde", + "serde-wasm-bindgen", "anyhow", "console_error_panic_hook", ] @@ -34,14 +37,16 @@ swc = { optional = true, version = "0.232.99", path = "../swc" } swc_common = { optional = true, version = "0.29.14", path = "../swc_common" } swc_ecma_ast = { optional = true, version = "0.94.19", path = "../swc_ecma_ast" } swc_ecma_transforms = { optional = true, version = "0.198.52", path = "../swc_ecma_transforms" } +swc_ecma_visit = { optional = true, version = "0.80.19", path = "../swc_ecma_visit" } # 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" } +serde = { optional = true, version = "1", features = ["derive"] } +serde-wasm-bindgen = { optional = true, version = "0.4.5" } wasm-bindgen = { optional = true, version = "0.2.82", features = [ - "serde-serialize", "enable-interning", ] } wasm-bindgen-futures = { optional = true, version = "0.4.32" } diff --git a/crates/binding_macros/src/wasm.rs b/crates/binding_macros/src/wasm.rs index a5e2aff01d3d..01fe2f99ac7c 100644 --- a/crates/binding_macros/src/wasm.rs +++ b/crates/binding_macros/src/wasm.rs @@ -6,24 +6,61 @@ use anyhow::Error; #[doc(hidden)] pub use js_sys; use once_cell::sync::Lazy; -use swc::{config::ErrorFormat, Compiler}; +#[doc(hidden)] +pub use serde_wasm_bindgen; +use serde_wasm_bindgen::Serializer; +use swc::{config::ErrorFormat, Compiler, HandlerOpts}; #[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}; +pub use swc_common::{ + comments::{self, SingleThreadedComments}, + errors::Handler, + FileName, Mark, GLOBALS, +}; +use swc_common::{sync::Lrc, FilePathMapping, SourceMap}; #[doc(hidden)] pub use swc_ecma_ast::{EsVersion, Program}; #[doc(hidden)] -pub use swc_ecma_transforms::pass::noop; +pub use swc_ecma_transforms::{pass::noop, resolver}; +#[doc(hidden)] +pub use swc_ecma_visit::VisitMutWith; #[doc(hidden)] pub use wasm_bindgen::{JsCast, JsValue}; #[doc(hidden)] pub use wasm_bindgen_futures::future_to_promise; +// A serializer with options to provide backward compat for the input / output +// from the bindgen generated swc interfaces. +#[doc(hidden)] +pub fn compat_serializer() -> Arc { + static V: Lazy> = Lazy::new(|| { + let s = Serializer::new() + .serialize_maps_as_objects(true) + .serialize_missing_as_null(true); + Arc::new(s) + }); + + V.clone() +} + +#[doc(hidden)] +pub fn try_with_handler_globals( + cm: Lrc, + config: HandlerOpts, + op: F, +) -> Result +where + F: FnOnce(&Handler) -> Result, +{ + GLOBALS.set(&Default::default(), || { + swc::try_with_handler(cm, config, op) + }) +} + /// Get global sourcemap pub fn compiler() -> Arc { console_error_panic_hook::set_once(); @@ -56,9 +93,11 @@ macro_rules! build_minify_sync { ($(#[$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> { + use serde::Serialize; + let c = $crate::wasm::compiler(); - $crate::wasm::try_with_handler( + $crate::wasm::try_with_handler_globals( c.cm.clone(), $opt, |handler| { @@ -66,13 +105,16 @@ macro_rules! build_minify_sync { let opts = if opts.is_null() || opts.is_undefined() { Default::default() } else { - $crate::wasm::anyhow::Context::context(opts.into_serde(), "failed to parse options")? + $crate::wasm::serde_wasm_bindgen::from_value(opts) + .map_err(|e| $crate::wasm::anyhow::anyhow!("failed to parse options: {}", e))? }; 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") + program + .serialize($crate::wasm::compat_serializer().as_ref()) + .map_err(|e| $crate::wasm::anyhow::anyhow!("failed to serialize program: {}", e)) }) }, ) @@ -105,9 +147,12 @@ macro_rules! build_parse_sync { ($(#[$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> { + use serde::Serialize; + use $crate::wasm::VisitMutWith; + let c = $crate::wasm::compiler(); - $crate::wasm::try_with_handler( + $crate::wasm::try_with_handler_globals( c.cm.clone(), $opt, |handler| { @@ -115,7 +160,8 @@ macro_rules! build_parse_sync { 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")? + $crate::wasm::serde_wasm_bindgen::from_value(opts) + .map_err(|e| $crate::wasm::anyhow::anyhow!("failed to parse options: {}", e))? }; let fm = c.cm.new_source_file($crate::wasm::FileName::Anon, s.into()); @@ -127,9 +173,8 @@ macro_rules! build_parse_sync { None }; - let program = $crate::wasm::anyhow::Context::context( - c - .parse_js( + let mut program = $crate::wasm::anyhow::Context::context( + c.parse_js( fm, handler, opts.target, @@ -140,7 +185,15 @@ macro_rules! build_parse_sync { "failed to parse code" )?; - $crate::wasm::anyhow::Context::context($crate::wasm::JsValue::from_serde(&program), "failed to serialize json") + program.visit_mut_with(&mut $crate::wasm::resolver( + $crate::wasm::Mark::new(), + $crate::wasm::Mark::new(), + opts.syntax.typescript(), + )); + + program + .serialize($crate::wasm::compat_serializer().as_ref()) + .map_err(|e| $crate::wasm::anyhow::anyhow!("failed to serialize program: {}", e)) }) }, ) @@ -175,7 +228,7 @@ macro_rules! build_print_sync { 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( + $crate::wasm::try_with_handler_globals( c.cm.clone(), $opt, |_handler| { @@ -183,10 +236,12 @@ macro_rules! build_print_sync { 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")? + $crate::wasm::serde_wasm_bindgen::from_value(opts) + .map_err(|e| $crate::wasm::anyhow::anyhow!("failed to parse options: {}", e))? }; - let program: $crate::wasm::Program = $crate::wasm::anyhow::Context::context(s.into_serde(), "failed to deserialize program")?; + let program: $crate::wasm::Program = $crate::wasm::serde_wasm_bindgen::from_value(s) + .map_err(|e| $crate::wasm::anyhow::anyhow!("failed to deserialize program: {}", e))?; let s = $crate::wasm::anyhow::Context::context(c .print( &program, @@ -205,7 +260,8 @@ macro_rules! build_print_sync { false, ),"failed to print code")?; - $crate::wasm::anyhow::Context::context(JsValue::from_serde(&s), "failed to serialize json") + serde_wasm_bindgen::to_value(&s) + .map_err(|e| anyhow::anyhow!("failed to serialize json: {}", e)) }) }, ) @@ -234,7 +290,7 @@ macro_rules! build_print { #[macro_export] macro_rules! build_transform_sync { ($(#[$m:meta])*) => { - build_transform_sync!($(#[$m])*, |_, _| $crate::wasm::noop(), |_, _| $crate::wasm::noop(), Default::default()); + 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()); @@ -247,6 +303,8 @@ macro_rules! build_transform_sync { opts: $crate::wasm::JsValue, experimental_plugin_bytes_resolver: $crate::wasm::JsValue, ) -> Result<$crate::wasm::JsValue, $crate::wasm::JsValue> { + use serde::Serialize; + let c = $crate::wasm::compiler(); #[cfg(feature = "plugin")] @@ -281,9 +339,7 @@ macro_rules! build_transform_sync { buffer }; - let bytes: Vec = data - .into_serde() - .expect("Could not read byte from plugin resolver"); + let bytes: Vec = $crate::wasm::serde_wasm_bindgen::from_value(data).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 @@ -296,13 +352,11 @@ macro_rules! build_transform_sync { 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))? + $crate::wasm::serde_wasm_bindgen::from_value(opts)? }; let error_format = opts.experimental.error_format.unwrap_or_default(); - - $crate::wasm::try_with_handler( + $crate::wasm::try_with_handler_globals( c.cm.clone(), $opt, |handler| { @@ -320,24 +374,25 @@ macro_rules! build_transform_sync { ); let cm = c.cm.clone(); let file = fm.clone(); - + let comments = $crate::wasm::SingleThreadedComments::default(); $crate::wasm::anyhow::Context::context( c.process_js_with_custom_pass( fm, None, handler, &opts, - Default::default(), + comments, $before_pass, $after_pass, ), "failed to process js file" )? } - Err(v) => unsafe { c.process_js(handler, v.into_serde().expect(""), &opts)? }, + Err(v) => unsafe { c.process_js(handler, $crate::wasm::serde_wasm_bindgen::from_value(v).expect(""), &opts)? }, }; - $crate::wasm::anyhow::Context::context($crate::wasm::JsValue::from_serde(&out), - "failed to serialize json") + out + .serialize($crate::wasm::compat_serializer().as_ref()) + .map_err(|e| $crate::wasm::anyhow::anyhow!("failed to serialize transform result: {}", e)) }) }, ) diff --git a/crates/swc_core/.gitignore b/crates/swc_core/.gitignore new file mode 100644 index 000000000000..137134b51ba1 --- /dev/null +++ b/crates/swc_core/.gitignore @@ -0,0 +1,2 @@ +tests/**/Cargo.lock +tests/**/target/ \ No newline at end of file diff --git a/crates/swc_core/Cargo.toml b/crates/swc_core/Cargo.toml index 8b9c06afef0a..8843784c8673 100644 --- a/crates/swc_core/Cargo.toml +++ b/crates/swc_core/Cargo.toml @@ -362,3 +362,6 @@ swc_plugin_runner = { optional = true, version = "0.77.33", path = "../swc_plugi [build-dependencies] vergen = { version = "7.3.2", default-features = false, features = ["cargo"] } + +[dev-dependencies] +anyhow = "1.0.66" diff --git a/crates/swc_core/tests/fixture/stub_napi/Cargo.toml b/crates/swc_core/tests/fixture/stub_napi/Cargo.toml new file mode 100644 index 000000000000..4a03aeefb7d2 --- /dev/null +++ b/crates/swc_core/tests/fixture/stub_napi/Cargo.toml @@ -0,0 +1,62 @@ +[workspace] + +[package] +edition = "2021" +name = "stub_napi" +publish = false +version = "0.1.0" + +[lib] +crate-type = ["cdylib"] + +[features] +default = ["swc_v1", "plugin"] +plugin = ["swc_core/plugin_transform_host_native"] +swc_v1 = ["swc_core/bundler_node_v1"] +swc_v2 = ["swc_core/bundler_node_v2"] + +# Experiemntal bytechecked plugin serialization / deserialization. +plugin_bytecheck = [ + # "swc_core/__plugin_transform_host_bytecheck", + "swc_core/__plugin_transform_host_schema_v1", + "swc_core/__plugin_transform_env_native", +] + +# Internal flag for testing purpose only. +__plugin_transform_vtest = [ + "swc_core/__plugin_transform_host", + "swc_core/__plugin_transform_host_schema_vtest", + "swc_core/__plugin_transform_env_native", +] + +[build-dependencies] +napi-build = { version = "2" } + +[dependencies] +anyhow = "1.0.66" +backtrace = "0.3" +napi = { version = "2", default-features = false, features = [ + "napi3", + "serde-json", +] } +napi-derive = { version = "2", default-features = false, features = [ + "type-def", +] } +path-clean = "0.1" +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1", features = ["unbounded_depth"] } +tracing = { version = "0.1.37", features = ["release_max_level_info"] } +tracing-chrome = "0.5.0" +tracing-futures = "0.2.5" +tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } + +swc_core = { path = "../../../../swc_core", features = [ + "ecma_ast", + "common_concurrent", + "bundler", + "ecma_loader", + "ecma_transforms", + "ecma_visit", + "base_node", + "base_concurrent", +] } diff --git a/crates/swc_core/tests/fixture/stub_napi/build.rs b/crates/swc_core/tests/fixture/stub_napi/build.rs new file mode 100644 index 000000000000..968e821ff76e --- /dev/null +++ b/crates/swc_core/tests/fixture/stub_napi/build.rs @@ -0,0 +1,29 @@ +use std::{ + env, + fs::File, + io::{BufWriter, Write}, + path::Path, +}; + +extern crate napi_build; + +#[cfg(all(not(feature = "swc_v1"), not(feature = "swc_v2")))] +compile_error!("Please enable swc_v1 or swc_v2 feature"); + +#[cfg(all(feature = "swc_v1", feature = "swc_v2"))] +compile_error!("Features swc_v1 and swc_v2 are incompatible"); + +fn main() { + let out_dir = env::var("OUT_DIR").expect("Outdir should exist"); + let dest_path = Path::new(&out_dir).join("triple.txt"); + let mut f = + BufWriter::new(File::create(&dest_path).expect("Failed to create target triple text")); + write!( + f, + "{}", + env::var("TARGET").expect("Target should be specified") + ) + .expect("Failed to write target triple text"); + + napi_build::setup(); +} diff --git a/crates/swc_core/tests/fixture/stub_napi/src/lib.rs b/crates/swc_core/tests/fixture/stub_napi/src/lib.rs new file mode 100644 index 000000000000..1bc338090d45 --- /dev/null +++ b/crates/swc_core/tests/fixture/stub_napi/src/lib.rs @@ -0,0 +1,52 @@ +#![recursion_limit = "2048"] +#![allow(dead_code)] + +#[macro_use] +extern crate napi_derive; + +use std::{env, panic::set_hook, sync::Arc}; + +use backtrace::Backtrace; +use swc_core::{ + base::Compiler, + common::{sync::Lazy, FilePathMapping, SourceMap}, +}; + +static COMPILER: Lazy> = Lazy::new(|| { + let cm = Arc::new(SourceMap::new(FilePathMapping::empty())); + + Arc::new(Compiler::new(cm)) +}); + +#[napi::module_init] +fn init() { + if cfg!(debug_assertions) || env::var("SWC_DEBUG").unwrap_or_default() == "1" { + set_hook(Box::new(|panic_info| { + let backtrace = Backtrace::new(); + println!("Panic: {:?}\nBacktrace: {:?}", panic_info, backtrace); + })); + } +} + +fn get_compiler() -> Arc { + COMPILER.clone() +} + +#[napi(js_name = "Compiler")] +pub struct JsCompiler { + _compiler: Arc, +} + +#[napi] +impl JsCompiler { + #[napi(constructor)] + #[allow(clippy::new_without_default)] + #[tracing::instrument(level = "info", skip_all)] + pub fn new() -> Self { + Self { + _compiler: COMPILER.clone(), + } + } +} + +pub type ArcCompiler = Arc; diff --git a/crates/swc_core/tests/fixture/stub_wasm/Cargo.toml b/crates/swc_core/tests/fixture/stub_wasm/Cargo.toml new file mode 100644 index 000000000000..62617bba8dea --- /dev/null +++ b/crates/swc_core/tests/fixture/stub_wasm/Cargo.toml @@ -0,0 +1,29 @@ +[workspace] + +[package] +edition = "2021" +name = "stub_wasm" +publish = false +version = "0.1.0" + +[lib] +crate-type = ["cdylib"] + +[features] +default = ["swc_v1"] +plugin = [] +swc_v1 = [] + +[dependencies] +anyhow = "1.0.66" +serde = { version = "1", features = ["derive"] } +serde-wasm-bindgen = "0.4.5" +swc_core = { path = "../../../../swc_core", features = [ + "common_perf", + "binding_macro_wasm", + "ecma_transforms", + "ecma_visit", + "plugin_transform_host_js", +] } +tracing = { version = "0.1.37", features = ["max_level_off"] } +wasm-bindgen = { version = "0.2.82", features = ["enable-interning"] } diff --git a/crates/swc_core/tests/fixture/stub_wasm/src/lib.rs b/crates/swc_core/tests/fixture/stub_wasm/src/lib.rs new file mode 100644 index 000000000000..f7269202a727 --- /dev/null +++ b/crates/swc_core/tests/fixture/stub_wasm/src/lib.rs @@ -0,0 +1,14 @@ +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::*; + +build_minify_sync!(#[wasm_bindgen(js_name = "minifySync")]); +build_minify!(#[wasm_bindgen(js_name = "minify")]); +build_parse_sync!(#[wasm_bindgen(js_name = "parseSync")]); +build_parse!(#[wasm_bindgen(js_name = "parse")]); +build_print_sync!(#[wasm_bindgen(js_name = "printSync")]); +build_print!(#[wasm_bindgen(js_name = "print")]); +build_transform_sync!(#[wasm_bindgen(js_name = "transformSync")]); +build_transform!(#[wasm_bindgen(js_name = "transform")]); diff --git a/crates/swc_core/tests/integration.rs b/crates/swc_core/tests/integration.rs new file mode 100644 index 000000000000..cb6f45d81ff4 --- /dev/null +++ b/crates/swc_core/tests/integration.rs @@ -0,0 +1,51 @@ +use std::{ + env, + path::{Path, PathBuf}, + process::{Command, Stdio}, +}; + +use anyhow::{anyhow, Error}; + +fn build_fixture_binary(dir: &Path, target: Option<&str>) -> Result<(), Error> { + let mut args = vec!["build".to_string()]; + if let Some(target) = target { + args.push(format!("--target={}", target)); + }; + + let mut cmd = Command::new("cargo"); + cmd.current_dir(dir); + cmd.args(args).stderr(Stdio::inherit()); + cmd.output()?; + + if !cmd + .status() + .expect("Exit code should be available") + .success() + { + return Err(anyhow!("Failed to build binary")); + } + + Ok(()) +} + +#[test] +fn swc_core_napi_integartion_build() -> Result<(), Error> { + build_fixture_binary( + &PathBuf::from(env::var("CARGO_MANIFEST_DIR")?) + .join("tests") + .join("fixture") + .join("stub_napi"), + None, + ) +} + +#[test] +fn swc_core_wasm_integartion_build() -> Result<(), Error> { + build_fixture_binary( + &PathBuf::from(env::var("CARGO_MANIFEST_DIR")?) + .join("tests") + .join("fixture") + .join("stub_wasm"), + Some("wasm32-unknown-unknown"), + ) +}