diff --git a/Cargo.lock b/Cargo.lock index 8b084c27b57..77e24687daa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -562,18 +562,13 @@ dependencies = [ ] [[package]] -name = "parcel-js-swc" +name = "parcel-js-swc-core" version = "0.1.0" dependencies = [ "Inflector", "data-encoding", "dunce", "indoc", - "jemallocator", - "mimalloc", - "napi", - "napi-build", - "napi-derive", "serde", "serde_bytes", "sha-1", @@ -583,6 +578,28 @@ dependencies = [ "swc_ecmascript", ] +[[package]] +name = "parcel-js-swc-napi" +version = "0.1.0" +dependencies = [ + "jemallocator", + "mimalloc", + "napi", + "napi-build", + "napi-derive", + "parcel-js-swc-core", +] + +[[package]] +name = "parcel-js-swc-wasm" +version = "0.1.0" +dependencies = [ + "js-sys", + "parcel-js-swc-core", + "serde-wasm-bindgen", + "wasm-bindgen", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -847,6 +864,18 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515286919c7a28f96c362f42cd42aec7ef6d417334118be69d1ad96160aa5f5" +dependencies = [ + "fnv", + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_bytes" version = "0.11.5" diff --git a/Cargo.toml b/Cargo.toml index c5e3cedef5d..440beec791c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] -members = ["packages/transformers/js", "packages/utils/fs-search"] - -[profile.release] -opt-level = 3 -# lto = true +members = [ + "packages/transformers/js/core", + "packages/transformers/js/napi", + "packages/transformers/js/wasm", + "packages/utils/fs-search", +] diff --git a/packages/transformers/js/Cargo.toml b/packages/transformers/js/core/Cargo.toml similarity index 57% rename from packages/transformers/js/Cargo.toml rename to packages/transformers/js/core/Cargo.toml index 0f2b82705ad..55068ed732d 100644 --- a/packages/transformers/js/Cargo.toml +++ b/packages/transformers/js/core/Cargo.toml @@ -1,14 +1,13 @@ [package] authors = ["Devon Govett "] -name = "parcel-js-swc" +name = "parcel-js-swc-core" version = "0.1.0" +edition = "2018" [lib] -crate-type = ["cdylib"] +crate-type = ["rlib"] [dependencies] -napi = { version = "1", features = ["serde-json"] } -napi-derive = "1" swc_ecmascript = { version = "0.36.0", features = ["parser", "transforms", "module", "optimization", "react", "typescript", "utils", "visit", "codegen", "utils"] } swc_ecma_preset_env = "0.20.1" swc_common = { version = "0.10.19", features = ["tty-emitter", "sourcemap"] } @@ -20,12 +19,3 @@ Inflector = "0.11.4" data-encoding = "2.3.2" sha-1 = "0.9.4" dunce = "1.0.1" - -[target.'cfg(target_os = "macos")'.dependencies] -jemallocator = { version = "0.3.2", features = ["disable_initial_exec_tls"] } - -[target.'cfg(windows)'.dependencies] -mimalloc = { version = "0.1.25", default-features = false } - -[build-dependencies] -napi-build = { version = "1" } diff --git a/packages/transformers/js/src/decl_collector.rs b/packages/transformers/js/core/src/decl_collector.rs similarity index 100% rename from packages/transformers/js/src/decl_collector.rs rename to packages/transformers/js/core/src/decl_collector.rs diff --git a/packages/transformers/js/src/dependency_collector.rs b/packages/transformers/js/core/src/dependency_collector.rs similarity index 99% rename from packages/transformers/js/src/dependency_collector.rs rename to packages/transformers/js/core/src/dependency_collector.rs index efbba67e457..08e4fa140dc 100644 --- a/packages/transformers/js/src/dependency_collector.rs +++ b/packages/transformers/js/core/src/dependency_collector.rs @@ -7,7 +7,7 @@ use swc_common::{SourceMap, SyntaxContext, DUMMY_SP}; use swc_ecmascript::ast; use swc_ecmascript::visit::{Fold, FoldWith}; -use utils::*; +use crate::utils::*; #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub enum DependencyKind { diff --git a/packages/transformers/js/src/env_replacer.rs b/packages/transformers/js/core/src/env_replacer.rs similarity index 99% rename from packages/transformers/js/src/env_replacer.rs rename to packages/transformers/js/core/src/env_replacer.rs index 3c9e6134cf3..fca81b2bcf3 100644 --- a/packages/transformers/js/src/env_replacer.rs +++ b/packages/transformers/js/core/src/env_replacer.rs @@ -5,7 +5,7 @@ use swc_common::{SyntaxContext, DUMMY_SP}; use swc_ecmascript::ast; use swc_ecmascript::visit::Fold; -use utils::*; +use crate::utils::*; pub struct EnvReplacer<'a> { pub replace_env: bool, diff --git a/packages/transformers/js/src/fs.rs b/packages/transformers/js/core/src/fs.rs similarity index 99% rename from packages/transformers/js/src/fs.rs rename to packages/transformers/js/core/src/fs.rs index e3a7e5f859c..58bb34acf19 100644 --- a/packages/transformers/js/src/fs.rs +++ b/packages/transformers/js/core/src/fs.rs @@ -1,7 +1,7 @@ +use crate::dependency_collector::{DependencyDescriptor, DependencyKind}; use crate::hoist::{Collect, Import}; use crate::utils::SourceLocation; use data_encoding::{BASE64, HEXLOWER}; -use dependency_collector::{DependencyDescriptor, DependencyKind}; use std::collections::HashSet; use std::path::{Path, PathBuf}; use swc_atoms::JsWord; diff --git a/packages/transformers/js/src/global_replacer.rs b/packages/transformers/js/core/src/global_replacer.rs similarity index 97% rename from packages/transformers/js/src/global_replacer.rs rename to packages/transformers/js/core/src/global_replacer.rs index 2224088a98d..7b4a5326175 100644 --- a/packages/transformers/js/src/global_replacer.rs +++ b/packages/transformers/js/core/src/global_replacer.rs @@ -6,8 +6,8 @@ use swc_common::{SourceMap, SyntaxContext, DUMMY_SP}; use swc_ecmascript::ast; use swc_ecmascript::visit::{Fold, FoldWith}; -use dependency_collector::{DependencyDescriptor, DependencyKind}; -use utils::{create_require, SourceLocation}; +use crate::dependency_collector::{DependencyDescriptor, DependencyKind}; +use crate::utils::{create_require, SourceLocation}; pub struct GlobalReplacer<'a> { pub source_map: &'a SourceMap, diff --git a/packages/transformers/js/src/hoist.rs b/packages/transformers/js/core/src/hoist.rs similarity index 100% rename from packages/transformers/js/src/hoist.rs rename to packages/transformers/js/core/src/hoist.rs diff --git a/packages/transformers/js/src/lib.rs b/packages/transformers/js/core/src/lib.rs similarity index 93% rename from packages/transformers/js/src/lib.rs rename to packages/transformers/js/core/src/lib.rs index d958a8da77f..f7046d38d76 100644 --- a/packages/transformers/js/src/lib.rs +++ b/packages/transformers/js/core/src/lib.rs @@ -1,6 +1,3 @@ -extern crate napi; -#[macro_use] -extern crate napi_derive; extern crate swc_common; extern crate swc_ecma_preset_env; extern crate swc_ecmascript; @@ -13,14 +10,6 @@ extern crate serde; extern crate serde_bytes; extern crate sha1; -#[cfg(target_os = "macos")] -#[global_allocator] -static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc; - -#[cfg(windows)] -#[global_allocator] -static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; - mod decl_collector; mod dependency_collector; mod env_replacer; @@ -30,7 +19,6 @@ mod hoist; mod modules; mod utils; -use napi::{CallContext, JsObject, JsUnknown, Result}; use std::collections::{HashMap, HashSet}; use std::str::FromStr; @@ -39,7 +27,6 @@ use swc_common::comments::SingleThreadedComments; use swc_common::errors::{DiagnosticBuilder, Emitter, Handler}; use swc_common::{chain, sync::Lrc, FileName, Globals, Mark, SourceMap}; use swc_ecma_preset_env::{preset_env, Mode::Entry, Targets, Version, Versions}; -use swc_ecmascript::ast; use swc_ecmascript::ast::Module; use swc_ecmascript::codegen::text_writer::JsWriter; use swc_ecmascript::parser::lexer::Lexer; @@ -62,7 +49,7 @@ use modules::esm2cjs; use utils::{CodeHighlight, Diagnostic, SourceLocation}; #[derive(Serialize, Debug, Deserialize)] -struct Config { +pub struct Config { filename: String, #[serde(with = "serde_bytes")] code: Vec, @@ -85,9 +72,9 @@ struct Config { } #[derive(Serialize, Debug, Deserialize, Default)] -struct TransformResult<'a> { +pub struct TransformResult { #[serde(with = "serde_bytes")] - code: &'a [u8], + code: Vec, map: Option, shebang: Option, dependencies: Vec, @@ -136,12 +123,8 @@ impl Emitter for ErrorBuffer { } } -#[js_function(1)] -fn transform(ctx: CallContext) -> Result { - let opts = ctx.get::(0)?; - let config: Config = ctx.env.from_js_value(opts)?; +pub fn transform(config: Config) -> Result { let mut result = TransformResult::default(); - let mut code_buf = vec![]; let mut map_buf = vec![]; let code = unsafe { std::str::from_utf8_unchecked(&config.code) }; @@ -197,7 +180,7 @@ fn transform(ctx: CallContext) -> Result { .collect(); result.diagnostics = Some(diagnostics); - ctx.env.to_js_value(&result) + Ok(result) } Ok((module, comments)) => { let mut module = module; @@ -333,7 +316,7 @@ fn transform(ctx: CallContext) -> Result { } Err(diagnostics) => { result.diagnostics = Some(diagnostics); - return ctx.env.to_js_value(&result); + return Ok(result); } } } else { @@ -360,9 +343,8 @@ fn transform(ctx: CallContext) -> Result { result.map = Some(String::from_utf8(map_buf).unwrap()); } } - code_buf = buf; - result.code = &code_buf; - ctx.env.to_js_value(&result) + result.code = buf; + Ok(result) }, ) }) @@ -413,7 +395,7 @@ fn emit( comments: SingleThreadedComments, program: &Module, source_maps: bool, -) -> Result<(Vec, Vec<(swc_common::BytePos, swc_common::LineCol)>)> { +) -> Result<(Vec, Vec<(swc_common::BytePos, swc_common::LineCol)>), std::io::Error> { let mut src_map_buf = vec![]; let mut buf = vec![]; { @@ -440,10 +422,3 @@ fn emit( return Ok((buf, src_map_buf)); } - -#[module_exports] -fn init(mut exports: JsObject) -> Result<()> { - exports.create_named_method("transform", transform)?; - - Ok(()) -} diff --git a/packages/transformers/js/src/modules.rs b/packages/transformers/js/core/src/modules.rs similarity index 100% rename from packages/transformers/js/src/modules.rs rename to packages/transformers/js/core/src/modules.rs diff --git a/packages/transformers/js/src/utils.rs b/packages/transformers/js/core/src/utils.rs similarity index 100% rename from packages/transformers/js/src/utils.rs rename to packages/transformers/js/core/src/utils.rs diff --git a/packages/transformers/js/napi/Cargo.toml b/packages/transformers/js/napi/Cargo.toml new file mode 100644 index 00000000000..a70e7b7469b --- /dev/null +++ b/packages/transformers/js/napi/Cargo.toml @@ -0,0 +1,22 @@ +[package] +authors = ["Devon Govett "] +name = "parcel-js-swc-napi" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +napi = { version = "1", features = ["serde-json"] } +napi-derive = "1" +parcel-js-swc-core = { path = "../core" } + +[target.'cfg(target_os = "macos")'.dependencies] +jemallocator = { version = "0.3.2", features = ["disable_initial_exec_tls"] } + +[target.'cfg(windows)'.dependencies] +mimalloc = { version = "0.1.25", default-features = false } + +[build-dependencies] +napi-build = { version = "1" } diff --git a/packages/transformers/js/build.rs b/packages/transformers/js/napi/build.rs similarity index 100% rename from packages/transformers/js/build.rs rename to packages/transformers/js/napi/build.rs diff --git a/packages/transformers/js/napi/src/lib.rs b/packages/transformers/js/napi/src/lib.rs new file mode 100644 index 00000000000..3bdd1206d27 --- /dev/null +++ b/packages/transformers/js/napi/src/lib.rs @@ -0,0 +1,30 @@ +extern crate napi; +#[macro_use] +extern crate napi_derive; +extern crate parcel_js_swc_core; + +use napi::{CallContext, JsObject, JsUnknown, Result}; + +#[cfg(target_os = "macos")] +#[global_allocator] +static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc; + +#[cfg(windows)] +#[global_allocator] +static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; + +#[js_function(1)] +fn transform(ctx: CallContext) -> Result { + let opts = ctx.get::(0)?; + let config: parcel_js_swc_core::Config = ctx.env.from_js_value(opts)?; + + let result = parcel_js_swc_core::transform(config)?; + ctx.env.to_js_value(&result) +} + +#[module_exports] +fn init(mut exports: JsObject) -> Result<()> { + exports.create_named_method("transform", transform)?; + + Ok(()) +} diff --git a/packages/transformers/js/native-browser.js b/packages/transformers/js/native-browser.js new file mode 100644 index 00000000000..e7283914440 --- /dev/null +++ b/packages/transformers/js/native-browser.js @@ -0,0 +1,37 @@ +import initFn, {transform} from './wasm/dist-web/parcel_js_swc_wasm.js'; + +export const init = initFn(); + +function transformWrapper(config) { + let result = transform(config); + return { + ...result, + // Hydrate Uint8Array into Buffer + code: Buffer.from(result.code.buffer), + // https://github.com/cloudflare/serde-wasm-bindgen/issues/10 + dependencies: result.dependencies?.map(d => ({ + ...d, + attributes: + d.attributes != null + ? Object.fromEntries([...d.attributes]) + : undefined, + })), + hoist_result: + result.hoist_result != null + ? { + ...result.hoist_result, + imported_symbols: Object.fromEntries([ + ...result.hoist_result.imported_symbols, + ]), + exported_symbols: Object.fromEntries([ + ...result.hoist_result.exported_symbols, + ]), + dynamic_imports: Object.fromEntries([ + ...result.hoist_result.dynamic_imports, + ]), + } + : undefined, + }; +} + +export {transformWrapper as transform}; diff --git a/packages/transformers/js/native.js b/packages/transformers/js/native.js new file mode 100644 index 00000000000..2078585ca48 --- /dev/null +++ b/packages/transformers/js/native.js @@ -0,0 +1,56 @@ +let parts = [process.platform, process.arch]; +if (process.platform === 'linux') { + const {MUSL, family} = require('detect-libc'); + if (family === MUSL) { + parts.push('musl'); + } else if (process.arch === 'arm') { + parts.push('gnueabihf'); + } else { + parts.push('gnu'); + } +} else if (process.platform === 'win32') { + parts.push('msvc'); +} + +let name = `parcel-swc.${parts.join('-')}.node`; +if (process.env.PARCEL_BUILD_ENV === 'production') { + module.exports = require(`./${name}`); +} else if (process.env.PARCEL_SWC_WASM) { + const {transform} = require('./wasm/dist-node/parcel_js_swc_wasm.js'); + + module.exports.transform = function(config) { + let result = transform(config); + return { + ...result, + // Hydrate Uint8Array into Buffer + code: Buffer.from(result.code.buffer), + // https://github.com/cloudflare/serde-wasm-bindgen/issues/10 + dependencies: result.dependencies?.map(d => ({ + ...d, + attributes: + d.attributes != null + ? Object.fromEntries([...d.attributes]) + : undefined, + })), + hoist_result: + result.hoist_result != null + ? { + ...result.hoist_result, + imported_symbols: Object.fromEntries([ + ...result.hoist_result.imported_symbols, + ]), + exported_symbols: Object.fromEntries([ + ...result.hoist_result.exported_symbols, + ]), + dynamic_imports: Object.fromEntries([ + ...result.hoist_result.dynamic_imports, + ]), + } + : undefined, + }; + }; +} else if (require('fs').existsSync(require('path').join(__dirname, name))) { + module.exports = require(`./${name}`); +} else { + module.exports = require(`self-published/${name}`); +} diff --git a/packages/transformers/js/package.json b/packages/transformers/js/package.json index 5c56cdc8a6b..819f028b9b7 100644 --- a/packages/transformers/js/package.json +++ b/packages/transformers/js/package.json @@ -46,7 +46,13 @@ "tiny-benchy": "^1.0.1" }, "scripts": { - "build": "napi build --platform", - "build-release": "napi build --platform --release" + "build": "napi build --platform --cargo-cwd napi", + "build-release": "napi build --platform --release --cargo-cwd napi", + "build:wasm-base": "wasm-pack build wasm --no-typescript --release", + "build:wasm-node": "yarn build:wasm-base --target nodejs --out-dir dist-node", + "build:wasm-web": "CARGO_PROFILE_RELEASE_LTO=true CARGO_PROFILE_RELEASE_PANIC=abort CARGO_PROFILE_RELEASE_OPT_LEVEL=z yarn build:wasm-base --target web --out-dir dist-web" + }, + "browser": { + "./native.js": "./native-browser.js" } } diff --git a/packages/transformers/js/src/JSTransformer.js b/packages/transformers/js/src/JSTransformer.js index 3e6fa25e78f..4d531487116 100644 --- a/packages/transformers/js/src/JSTransformer.js +++ b/packages/transformers/js/src/JSTransformer.js @@ -3,7 +3,7 @@ import type {JSONObject, EnvMap} from '@parcel/types'; import type {SchemaEntity} from '@parcel/utils'; import SourceMap from '@parcel/source-map'; import {Transformer} from '@parcel/plugin'; -import {transform} from './native'; +import {init, transform} from '../native'; import {isURL} from '@parcel/utils'; import path from 'path'; import browserslist from 'browserslist'; @@ -159,8 +159,11 @@ export default (new Transformer({ asset.isSplittable = true; } - let code = await asset.getBuffer(); - let originalMap = await asset.getMap(); + let [code, originalMap] = await Promise.all([ + asset.getBuffer(), + asset.getMap(), + init, + ]); let targets; if (asset.isSource) { diff --git a/packages/transformers/js/src/native.js b/packages/transformers/js/src/native.js deleted file mode 100644 index aec5859ffde..00000000000 --- a/packages/transformers/js/src/native.js +++ /dev/null @@ -1,24 +0,0 @@ -let parts = [process.platform, process.arch]; -if (process.platform === 'linux') { - const {MUSL, family} = require('detect-libc'); - if (family === MUSL) { - parts.push('musl'); - } else if (process.arch === 'arm') { - parts.push('gnueabihf'); - } else { - parts.push('gnu'); - } -} else if (process.platform === 'win32') { - parts.push('msvc'); -} - -let name = `parcel-swc.${parts.join('-')}.node`; -if (process.env.PARCEL_BUILD_ENV === 'production') { - module.exports = require(`../${name}`); -} else if ( - require('fs').existsSync(require('path').join(__dirname, '..', name)) -) { - module.exports = require(`../${name}`); -} else { - module.exports = require(`self-published/${name}`); -} diff --git a/packages/transformers/js/wasm/Cargo.toml b/packages/transformers/js/wasm/Cargo.toml new file mode 100644 index 00000000000..24d2e79095e --- /dev/null +++ b/packages/transformers/js/wasm/Cargo.toml @@ -0,0 +1,14 @@ +[package] +authors = ["Devon Govett "] +name = "parcel-js-swc-wasm" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +js-sys = "0.3" +parcel-js-swc-core = { path = "../core" } +serde-wasm-bindgen = "0.2.0" +wasm-bindgen = "0.2" diff --git a/packages/transformers/js/wasm/src/lib.rs b/packages/transformers/js/wasm/src/lib.rs new file mode 100644 index 00000000000..44f13e3853e --- /dev/null +++ b/packages/transformers/js/wasm/src/lib.rs @@ -0,0 +1,14 @@ +extern crate parcel_js_swc_core; + +use js_sys::Error; +use serde_wasm_bindgen; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn transform(config_val: JsValue) -> Result { + let config: parcel_js_swc_core::Config = + serde_wasm_bindgen::from_value(config_val).map_err(|err| JsValue::from(err))?; + let result = parcel_js_swc_core::transform(config) + .map_err(|e| Error::from(JsValue::from_str(&e.to_string())))?; + Ok(serde_wasm_bindgen::to_value(&result).map_err(|err| JsValue::from(err))?) +}