Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(binding/wasm): use binding_macro #6487

Merged
merged 1 commit into from Nov 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
247 changes: 12 additions & 235 deletions bindings/binding_core_wasm/src/lib.rs
@@ -1,34 +1,10 @@
use anyhow::Error;
use serde::Serialize;
use serde_wasm_bindgen::Serializer;
use swc_core::{
base::HandlerOpts,
binding_macros::wasm::{
compiler, convert_err, future_to_promise,
js_sys::{JsString, Promise},
noop, Options, ParseOptions, SourceMapsConfig,
},
common::{
comments::{self, SingleThreadedComments},
errors::Handler,
sync::Lrc,
FileName, Mark, SourceMap, GLOBALS,
},
ecma::{
ast::{EsVersion, Program},
transforms::base::resolver,
visit::VisitMutWith,
},
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::prelude::*;
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)]
Expand Down Expand Up @@ -68,210 +44,11 @@ export function transform(
export function transformSync(code: string | Program, opts?: Options, experimental_plugin_bytes_resolver?: any): Output;
"#;

fn try_with_handler<F, Ret>(cm: Lrc<SourceMap>, config: HandlerOpts, op: F) -> Result<Ret, Error>
where
F: FnOnce(&Handler) -> Result<Ret, Error>,
{
GLOBALS.set(&Default::default(), || {
swc_core::base::try_with_handler(cm, config, op)
})
}

#[wasm_bindgen(
js_name = "minifySync",
typescript_type = "minifySync",
skip_typescript
)]
pub fn minify_sync(s: JsString, opts: JsValue) -> Result<JsValue, JsValue> {
let c = compiler();
try_with_handler(c.cm.clone(), Default::default(), |handler| {
c.run(|| {
let opts = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
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")?;

program
.serialize(&COMPAT_SERIALIZER)
.map_err(|e| anyhow::anyhow!("failed to serialize program: {}", e))
})
})
.map_err(|e| convert_err(e, None))
}

#[wasm_bindgen(js_name = "minify", typescript_type = "minify", skip_typescript)]
pub fn minify(s: JsString, opts: JsValue) -> Promise {
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> {
let c = compiler();
try_with_handler(c.cm.clone(), Default::default(), |handler| {
c.run(|| {
GLOBALS.set(&Default::default(), || {
let opts: ParseOptions = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
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();
let comments = if opts.comments {
Some(&cmts as &dyn comments::Comments)
} else {
None
};
let mut program = anyhow::Context::context(
c.parse_js(
fm,
handler,
opts.target,
opts.syntax,
opts.is_module,
comments,
),
"failed to parse code",
)?;

program.visit_mut_with(&mut resolver(
Mark::new(),
Mark::new(),
opts.syntax.typescript(),
));

program
.serialize(&COMPAT_SERIALIZER)
.map_err(|e| anyhow::anyhow!("failed to serialize program: {}", e))
})
})
})
.map_err(|e| convert_err(e, None))
}

#[wasm_bindgen(js_name = "parse", typescript_type = "parse", skip_typescript)]
pub fn parse(s: JsString, opts: JsValue) -> Promise {
future_to_promise(async { parse_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> {
let c = compiler();
let opts: Options = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
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(|| {
let s = JsCast::dyn_into::<JsString>(s);
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(),
);
let cm = c.cm.clone();
let file = fm.clone();
let comments = SingleThreadedComments::default();
anyhow::Context::context(
c.process_js_with_custom_pass(
fm,
None,
handler,
&opts,
comments,
|_| noop(),
|_| noop(),
),
"failed to process js file",
)?
}
Err(v) => {
c.process_js(handler, serde_wasm_bindgen::from_value(v).expect("Should able to deserialize into program"), &opts)?
}
};

out.serialize(&COMPAT_SERIALIZER)
.map_err(|e| anyhow::anyhow!("failed to serialize transform result: {}", e))
})
})
.map_err(|e| convert_err(e, Some(error_format)))
}

#[wasm_bindgen(js_name = "transform", typescript_type = "transform", skip_typescript)]
pub fn transform(
s: JsValue,
opts: JsValue,
experimental_plugin_bytes_resolver: JsValue,
) -> Promise {
future_to_promise(async { transform_sync(s, opts, experimental_plugin_bytes_resolver) })
}

#[wasm_bindgen(js_name = "printSync", typescript_type = "printSync", skip_typescript)]
pub fn print_sync(s: JsValue, opts: JsValue) -> Result<JsValue, JsValue> {
let c = compiler();
try_with_handler(c.cm.clone(), Default::default(), |_handler| {
c.run(|| {
let opts: Options = if opts.is_null() || opts.is_undefined() {
Default::default()
} else {
serde_wasm_bindgen::from_value(opts)
.map_err(|e| anyhow::anyhow!("failed to parse options: {}", e))?
};

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,
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,
),
"failed to print code",
)?;

serde_wasm_bindgen::to_value(&s)
.map_err(|e| anyhow::anyhow!("failed to serialize json: {}", e))
})
})
.map_err(|e| convert_err(e, None))
}

#[wasm_bindgen(js_name = "print", typescript_type = "print", skip_typescript)]
pub fn print(s: JsValue, opts: JsValue) -> Promise {
future_to_promise(async { print_sync(s, opts) })
}
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)]);
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)]);
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)]);
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)]);
17 changes: 11 additions & 6 deletions bindings/binding_core_wasm/src/types.rs
Expand Up @@ -364,11 +364,11 @@ export interface TerserCompressOptions {
export interface TerserMangleOptions {
props?: TerserManglePropertiesOptions,

top_level?: boolean,
toplevel?: boolean,

keep_class_names?: boolean,
keep_classnames?: boolean,

keep_fn_names?: boolean,
keep_fnames?: boolean,

keep_private_props?: boolean,

Expand Down Expand Up @@ -678,7 +678,7 @@ export interface JscConfig {
baseUrl?: string

paths?: {
[from: string]: [string]
[from: string]: string[]
}

minify?: JsMinifyOptions;
Expand Down Expand Up @@ -907,7 +907,7 @@ export interface GlobalPassOption {
envs?: string[];
}

export type ModuleConfig = Es6Config | CommonJsConfig | UmdConfig | AmdConfig | NodeNextConfig;
export type ModuleConfig = Es6Config | CommonJsConfig | UmdConfig | AmdConfig | NodeNextConfig | SystemjsConfig;

export interface BaseModuleConfig {
/**
Expand Down Expand Up @@ -1068,6 +1068,8 @@ export interface BaseModuleConfig {
* If set to true, dynamic imports will be preserved.
*/
ignoreDynamic?: boolean;
allowTopLevelThis?: boolean;
preserveImportMeta?: boolean;
}

export interface Es6Config extends BaseModuleConfig {
Expand All @@ -1091,7 +1093,10 @@ export interface AmdConfig extends BaseModuleConfig {
type: "amd";
moduleId?: string;
}

export interface SystemjsConfig {
type: "systemjs";
allowTopLevelThis?: boolean;
}
export interface Output {
/**
* Transformed code
Expand Down