mirror of
https://github.com/swc-project/swc.git
synced 2024-12-23 13:51:19 +03:00
Wasm (#691)
This commit is contained in:
parent
37bfb79b02
commit
14f5212d3d
@ -7,3 +7,9 @@ rustflags = [
|
||||
rustdocflags = [
|
||||
"--cfg", "procmacro2_semver_exempt",
|
||||
]
|
||||
|
||||
[target.wasm32-unknown-unknown]
|
||||
rustflags = [
|
||||
"--cfg", "procmacro2_semver_exempt",
|
||||
"-C", "link-args=-z stack-size=16777216",
|
||||
]
|
@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
members = ["ecmascript/visit"]
|
||||
members = ["ecmascript/visit", "wasm"]
|
||||
|
||||
[package]
|
||||
name = "swc"
|
||||
|
@ -1,6 +1,8 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use pmutil::{synom_ext::FromSpan, SpanExt};
|
||||
use pmutil::synom_ext::FromSpan;
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
use pmutil::SpanExt;
|
||||
use proc_macro2::Span;
|
||||
use syn::*;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"browserslist": "^4.8.7",
|
||||
"jest": "^25.1.0",
|
||||
"sourcemap-validator": "^2.1.0"
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ pub struct Options {
|
||||
#[serde(flatten, default)]
|
||||
pub config: Option<Config>,
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[serde(default = "default_cwd")]
|
||||
pub cwd: PathBuf,
|
||||
|
||||
@ -73,6 +74,7 @@ pub struct Options {
|
||||
#[serde(default = "default_swcrc")]
|
||||
pub swcrc: bool,
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[serde(default)]
|
||||
pub swcrc_roots: Option<PathBuf>,
|
||||
|
||||
@ -284,6 +286,7 @@ pub struct CallerOptions {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn default_cwd() -> PathBuf {
|
||||
::std::env::current_dir().unwrap()
|
||||
}
|
||||
@ -645,12 +648,17 @@ impl GlobalPassOption {
|
||||
let envs = self.envs;
|
||||
InlineGlobals {
|
||||
globals: mk_map(cm, handler, self.vars.into_iter(), false),
|
||||
envs: mk_map(
|
||||
|
||||
envs: if cfg!(target_arch = "wasm32") {
|
||||
mk_map(cm, handler, vec![].into_iter(), true)
|
||||
} else {
|
||||
mk_map(
|
||||
cm,
|
||||
handler,
|
||||
env::vars().filter(|(k, _)| envs.contains(&*k)),
|
||||
true,
|
||||
),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
16
src/lib.rs
16
src/lib.rs
@ -35,7 +35,11 @@ pub use ecmascript::{
|
||||
transforms::{chain_at, pass::Pass},
|
||||
};
|
||||
use serde::Serialize;
|
||||
use std::{fs::File, path::Path, sync::Arc};
|
||||
use std::{
|
||||
fs::File,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
pub struct Compiler {
|
||||
/// swc uses rustc's span interning.
|
||||
@ -285,9 +289,13 @@ impl Compiler {
|
||||
is_module,
|
||||
..
|
||||
} = opts;
|
||||
let root = root
|
||||
.clone()
|
||||
.unwrap_or_else(|| ::std::env::current_dir().unwrap());
|
||||
let root = root.clone().unwrap_or_else(|| {
|
||||
if cfg!(target_arch = "wasm32") {
|
||||
PathBuf::new()
|
||||
} else {
|
||||
::std::env::current_dir().unwrap()
|
||||
}
|
||||
});
|
||||
|
||||
let config_file = match config_file {
|
||||
Some(ConfigFile::Str(ref s)) => {
|
||||
|
24
wasm/Cargo.toml
Normal file
24
wasm/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "wasm"
|
||||
version = "0.1.0"
|
||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||
license = "Apache-2.0/MIT"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
description = "wasm module for swc"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
swc = { path = "../" }
|
||||
serde_json = "1"
|
||||
once_cell = "1.3.1"
|
||||
path-clean = "0.1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
wasm-bindgen-futures = "0.4.8"
|
||||
console_error_panic_hook = "0.1.6"
|
||||
|
||||
[dependencies.wasm-bindgen]
|
||||
version = "0.2"
|
||||
features = ["serde-serialize"]
|
11
wasm/__tests__/error.js
Normal file
11
wasm/__tests__/error.js
Normal file
@ -0,0 +1,11 @@
|
||||
const swc = require("../pkg");
|
||||
|
||||
it("properly reports error", function () {
|
||||
expect(() => {
|
||||
swc.transformSync("Foo {}", {});
|
||||
}).toThrow("failed to process code: failed to parse module");
|
||||
|
||||
expect(() => {
|
||||
swc.transformSync("Foo {}", {});
|
||||
}).toThrow("error: Expected ';', '}' or <eof>");
|
||||
});
|
5
wasm/__tests__/simple.js
Normal file
5
wasm/__tests__/simple.js
Normal file
@ -0,0 +1,5 @@
|
||||
const swc = require("../pkg");
|
||||
|
||||
it("should be loadable", function () {
|
||||
const output = swc.transformSync("class Foo {}", {});
|
||||
});
|
5
wasm/package.json
Normal file
5
wasm/package.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"jest": "^25.1.0"
|
||||
}
|
||||
}
|
1
wasm/scripts/build.sh
Executable file
1
wasm/scripts/build.sh
Executable file
@ -0,0 +1 @@
|
||||
wasm-pack build --debug --scope swc -t nodejs
|
6
wasm/scripts/test.sh
Executable file
6
wasm/scripts/test.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
|
||||
./scripts/build.sh
|
||||
npx jest
|
162
wasm/src/lib.rs
Normal file
162
wasm/src/lib.rs
Normal file
@ -0,0 +1,162 @@
|
||||
#![feature(box_syntax)]
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{
|
||||
fmt::{self, Display, Formatter},
|
||||
io::{self, Write},
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
use swc::{
|
||||
common::{
|
||||
errors::{EmitterWriter, Handler, HandlerFlags, SourceMapperDyn},
|
||||
FileName, FilePathMapping, SourceMap,
|
||||
},
|
||||
config::{InputSourceMap, Options, ParseOptions, SourceMapsConfig},
|
||||
ecmascript::ast::Program,
|
||||
Compiler,
|
||||
};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen(js_name = "parseSync")]
|
||||
pub fn parse_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
let opts: ParseOptions = opts
|
||||
.into_serde()
|
||||
.map_err(|err| format!("failed to parse options: {}", err))?;
|
||||
|
||||
let (c, errors) = compiler();
|
||||
|
||||
let fm = c.cm.new_source_file(FileName::Anon, s.into());
|
||||
let (prog, src_map) = c
|
||||
.parse_js(
|
||||
fm,
|
||||
opts.target,
|
||||
opts.syntax,
|
||||
opts.is_module,
|
||||
opts.comments,
|
||||
&InputSourceMap::Bool(false),
|
||||
)
|
||||
.map_err(|err| format!("failed to parse: {}\n{}", err, errors))?;
|
||||
|
||||
let mut source_map = vec![];
|
||||
if let Some(src_map) = src_map {
|
||||
src_map
|
||||
.to_writer(&mut source_map)
|
||||
.map_err(|err| format!("failed to print source map file: {}", err))?;
|
||||
}
|
||||
|
||||
Ok(
|
||||
JsValue::from_serde(&(prog, &*String::from_utf8_lossy(&source_map)))
|
||||
.map_err(|err| format!("failed to return value: {}", err))?,
|
||||
)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "printSync")]
|
||||
pub fn print_sync(s: JsValue, opts: JsValue) -> Result<JsValue, JsValue> {
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
let program: Program = s
|
||||
.into_serde()
|
||||
.map_err(|err| format!("not a program: {}", err))?;
|
||||
|
||||
let opts: Options = opts
|
||||
.into_serde()
|
||||
.map_err(|err| format!("failed to parse options: {}", err))?;
|
||||
|
||||
let (c, errors) = compiler();
|
||||
|
||||
let s = c
|
||||
.print(
|
||||
&program,
|
||||
c.comments(),
|
||||
opts.source_maps
|
||||
.clone()
|
||||
.unwrap_or(SourceMapsConfig::Bool(false)),
|
||||
None,
|
||||
opts.config.unwrap_or_default().minify.unwrap_or_default(),
|
||||
)
|
||||
.map_err(|err| format!("failed to print: {}\n{}", err, errors))?;
|
||||
|
||||
Ok(JsValue::from_serde(&s).map_err(|err| format!("failed to print: {}\n{}", err, errors))?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "transformSync")]
|
||||
pub fn transform_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
let opts: Options = opts
|
||||
.into_serde()
|
||||
.map_err(|err| format!("failed to parse options: {}", err))?;
|
||||
|
||||
let (c, errors) = compiler();
|
||||
|
||||
let fm = c.cm.new_source_file(FileName::Anon, s.into());
|
||||
let out = c
|
||||
.process_js_file(fm, &opts)
|
||||
.map_err(|err| format!("failed to process code: {}\n{}", err, errors))?;
|
||||
|
||||
Ok(JsValue::from_serde(&out).unwrap())
|
||||
}
|
||||
|
||||
fn compiler() -> (Compiler, BufferedError) {
|
||||
let cm = codemap();
|
||||
|
||||
let (handler, errors) = new_handler(cm.clone());
|
||||
|
||||
let c = Compiler::new(cm.clone(), handler);
|
||||
|
||||
(c, errors)
|
||||
}
|
||||
|
||||
/// Get global sourcemap
|
||||
fn codemap() -> Arc<SourceMap> {
|
||||
static CM: Lazy<Arc<SourceMap>> =
|
||||
Lazy::new(|| Arc::new(SourceMap::new(FilePathMapping::empty())));
|
||||
|
||||
CM.clone()
|
||||
}
|
||||
|
||||
/// Creates a new handler which emits to returned buffer.
|
||||
fn new_handler(cm: Arc<SourceMapperDyn>) -> (Handler, BufferedError) {
|
||||
let e = BufferedError::default();
|
||||
|
||||
let handler = Handler::with_emitter_and_flags(
|
||||
Box::new(EmitterWriter::new(
|
||||
Box::new(e.clone()),
|
||||
Some(cm.clone()),
|
||||
false,
|
||||
false,
|
||||
)),
|
||||
HandlerFlags {
|
||||
treat_err_as_bug: false,
|
||||
can_emit_warnings: true,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
(handler, e)
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct BufferedError(Arc<RwLock<String>>);
|
||||
|
||||
impl Write for BufferedError {
|
||||
fn write(&mut self, d: &[u8]) -> io::Result<usize> {
|
||||
self.0
|
||||
.write()
|
||||
.unwrap()
|
||||
.push_str(&String::from_utf8_lossy(d));
|
||||
|
||||
Ok(d.len())
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for BufferedError {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
Display::fmt(&self.0.read().unwrap(), f)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user