You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
4.6 KiB
Rust

// From https://github.com/swc-project/swc/blob/main/crates/binding_core_wasm/src/lib.rs
use anyhow::{Context, Error};
use once_cell::sync::Lazy;
use std::sync::Arc;
use swc::{
config::{
ErrorFormat, JsMinifyOptions, Options, ParseOptions, SourceMapsConfig,
},
try_with_handler, Compiler,
};
use swc_common::{comments::Comments, FileName, FilePathMapping, SourceMap};
use swc_ecmascript::ast::{EsVersion, Program};
use wasm_bindgen::prelude::*;
fn convert_err(err: Error, error_format: ErrorFormat) -> JsValue {
error_format.format(&err).into()
}
#[wasm_bindgen(js_name = "minifySync")]
pub fn minify_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
console_error_panic_hook::set_once();
let c = compiler();
try_with_handler(
c.cm.clone(),
swc::HandlerOpts {
..Default::default()
},
|handler| {
c.run(|| {
let opts: JsMinifyOptions =
opts.into_serde().context("failed to parse options")?;
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 = "parseSync")]
pub fn parse_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
console_error_panic_hook::set_once();
let c = compiler();
try_with_handler(
c.cm.clone(),
swc::HandlerOpts {
..Default::default()
},
|handler| {
c.run(|| {
let opts: ParseOptions =
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 = "printSync")]
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::HandlerOpts {
..Default::default()
},
|_handler| {
c.run(|| {
let opts: Options =
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 = "transformSync")]
pub fn transform_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
console_error_panic_hook::set_once();
let c = compiler();
let opts: Options = 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::HandlerOpts {
..Default::default()
},
|handler| {
c.run(|| {
let fm = c.cm.new_source_file(
if opts.filename.is_empty() {
FileName::Anon
} else {
FileName::Real(opts.filename.clone().into())
},
s.into(),
);
let out = c
.process_js_file(fm, handler, &opts)
.context("failed to process input file")?;
JsValue::from_serde(&out).context("failed to serialize json")
})
},
)
.map_err(|e| convert_err(e, error_format))
}
/// 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()
}