// 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 { 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 { 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 { 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 { 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 { static C: Lazy> = Lazy::new(|| { let cm = Arc::new(SourceMap::new(FilePathMapping::empty())); Arc::new(Compiler::new(cm)) }); C.clone() }