Skip to content

Commit

Permalink
feat(next-swc/wasm): export async interfaces (#39231)
Browse files Browse the repository at this point in the history
* build(cargo): update dependencies

* feat(next-swc/wasm): export async interfaces

* feat(next/swc): use async wasm binding interface

* refactor(next/swc): allow to fallback for non-async published pkg

* Apply suggestions from code review

Co-authored-by: JJ Kasper <jj@jjsweb.site>
  • Loading branch information
kwonoj and ijjk committed Aug 4, 2022
1 parent de41597 commit 58b920d
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 46 deletions.
27 changes: 14 additions & 13 deletions packages/next-swc/Cargo.lock

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

3 changes: 2 additions & 1 deletion packages/next-swc/crates/wasm/Cargo.toml
Expand Up @@ -30,8 +30,9 @@ swc_common = { version = "0.26.0", features = ["concurrent", "sourcemap"] }
swc_ecmascript = { version = "0.186.0", features = ["codegen", "minifier", "optimization", "parser", "react", "transforms", "typescript", "utils", "visit"] }
swc_plugin_runner = { version = "0.70.0", default-features = false, optional = true }
tracing = { version = "0.1.32", features = ["release_max_level_off"] }
wasm-bindgen = {version = "0.2", features = ["serde-serialize"]}
wasm-bindgen = {version = "0.2", features = ["serde-serialize", "enable-interning"]}
wasm-bindgen-futures = "0.4.8"
wasmer = { version = "2.3.0", optional = true, default-features = false }
wasmer-wasi = { version = "2.3.0", optional = true, default-features = false }
getrandom = { version = "0.2.5", optional = true, default-features = false }
js-sys = "0.3.59"
86 changes: 60 additions & 26 deletions packages/next-swc/crates/wasm/src/lib.rs
@@ -1,18 +1,20 @@
use anyhow::{Context, Error};
use js_sys::JsString;
use next_swc::{custom_before_pass, TransformOptions};
use once_cell::sync::Lazy;
use std::sync::Arc;
use swc::{config::JsMinifyOptions, config::ParseOptions, try_with_handler, Compiler};
use swc_common::{comments::Comments, errors::ColorConfig, FileName, FilePathMapping, SourceMap};
use swc_ecmascript::transforms::pass::noop;
use wasm_bindgen::prelude::*;
use wasm_bindgen::{prelude::*, JsCast};
use wasm_bindgen_futures::future_to_promise;

fn convert_err(err: Error) -> JsValue {
format!("{:?}", err).into()
}

#[wasm_bindgen(js_name = "minifySync")]
pub fn minify_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
pub fn minify_sync(s: JsString, opts: JsValue) -> Result<JsValue, JsValue> {
console_error_panic_hook::set_once();

let c = compiler();
Expand All @@ -37,8 +39,15 @@ pub fn minify_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
.map_err(convert_err)
}

#[wasm_bindgen(js_name = "minify")]
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 = "transformSync")]
pub fn transform_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
pub fn transform_sync(s: JsValue, opts: JsValue) -> Result<JsValue, JsValue> {
console_error_panic_hook::set_once();

let c = compiler();
Expand All @@ -52,37 +61,55 @@ pub fn transform_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
|handler| {
let opts: TransformOptions = opts.into_serde().context("failed to parse options")?;

let fm = c.cm.new_source_file(
if opts.swc.filename.is_empty() {
FileName::Anon
} else {
FileName::Real(opts.swc.filename.clone().into())
},
s.into(),
);
let cm = c.cm.clone();
let file = fm.clone();
let out = c
.process_js_with_custom_pass(
fm,
None,
handler,
&opts.swc,
|_, comments| {
custom_before_pass(cm, file, &opts, comments.clone(), Default::default())
},
|_, _| noop(),
)
.context("failed to process js file")?;
let s = s.dyn_into::<js_sys::JsString>();
let out = match s {
Ok(s) => {
let fm = c.cm.new_source_file(
if opts.swc.filename.is_empty() {
FileName::Anon
} else {
FileName::Real(opts.swc.filename.clone().into())
},
s.into(),
);
let cm = c.cm.clone();
let file = fm.clone();
c.process_js_with_custom_pass(
fm,
None,
handler,
&opts.swc,
|_, comments| {
custom_before_pass(
cm,
file,
&opts,
comments.clone(),
Default::default(),
)
},
|_, _| noop(),
)
.context("failed to process js file")?
}
Err(v) => c.process_js(handler, v.into_serde().expect(""), &opts.swc)?,
};

JsValue::from_serde(&out).context("failed to serialize json")
},
)
.map_err(convert_err)
}

#[wasm_bindgen(js_name = "transform")]
pub fn transform(s: JsValue, opts: 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) })
}

#[wasm_bindgen(js_name = "parseSync")]
pub fn parse_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
pub fn parse_sync(s: JsString, opts: JsValue) -> Result<JsValue, JsValue> {
console_error_panic_hook::set_once();

let c = swc::Compiler::new(Arc::new(SourceMap::new(FilePathMapping::empty())));
Expand Down Expand Up @@ -124,6 +151,13 @@ pub fn parse_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
.map_err(convert_err)
}

#[wasm_bindgen(js_name = "parse")]
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) })
}

/// Get global sourcemap
fn compiler() -> Arc<Compiler> {
static C: Lazy<Arc<Compiler>> = Lazy::new(|| {
Expand Down
16 changes: 10 additions & 6 deletions packages/next/build/swc/index.js
Expand Up @@ -143,22 +143,26 @@ async function loadWasm(importPath = '') {
wasmBindings = {
isWasm: true,
transform(src, options) {
return Promise.resolve(
bindings.transformSync(src.toString(), options)
)
// TODO: we can remove fallback to sync interface once new stable version of next-swc gets published (current v12.2)
return bindings?.transform
? bindings.transform(src.toString(), options)
: Promise.resolve(bindings.transformSync(src.toString(), options))
},
transformSync(src, options) {
return bindings.transformSync(src.toString(), options)
},
minify(src, options) {
return Promise.resolve(bindings.minifySync(src.toString(), options))
return bindings?.minify
? bindings.minify(src.toString(), options)
: Promise.resolve(bindings.minifySync(src.toString(), options))
},
minifySync(src, options) {
return bindings.minifySync(src.toString(), options)
},
parse(src, options) {
const astStr = bindings.parseSync(src.toString(), options)
return Promise.resolve(astStr)
return bindings?.parse
? bindings.parse(src.toString(), options)
: Promise.resolve(bindings.parseSync(src.toString(), options))
},
parseSync(src, options) {
const astStr = bindings.parseSync(src.toString(), options)
Expand Down

0 comments on commit 58b920d

Please sign in to comment.