This commit is contained in:
강동윤 2020-05-07 18:17:57 +09:00 committed by GitHub
parent 37bfb79b02
commit 14f5212d3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 534 additions and 295 deletions

View File

@ -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",
]

View File

@ -1,5 +1,5 @@
[workspace]
members = ["ecmascript/visit"]
members = ["ecmascript/visit", "wasm"]
[package]
name = "swc"

File diff suppressed because it is too large Load Diff

View File

@ -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::*;

View File

@ -1,6 +1,7 @@
{
"devDependencies": {
"browserslist": "^4.8.7",
"jest": "^25.1.0",
"sourcemap-validator": "^2.1.0"
}
}

View File

@ -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(
cm,
handler,
env::vars().filter(|(k, _)| envs.contains(&*k)),
true,
),
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,
)
},
}
}
}

View File

@ -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
View 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
View 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
View 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
View File

@ -0,0 +1,5 @@
{
"devDependencies": {
"jest": "^25.1.0"
}
}

1
wasm/scripts/build.sh Executable file
View File

@ -0,0 +1 @@
wasm-pack build --debug --scope swc -t nodejs

6
wasm/scripts/test.sh Executable file
View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -eu
./scripts/build.sh
npx jest

162
wasm/src/lib.rs Normal file
View 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)
}
}