diff --git a/Cargo.lock b/Cargo.lock index 2198c682c4c..6b83f390247 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1225,9 +1225,9 @@ checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "js-sys" -version = "0.3.55" +version = "0.3.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" dependencies = [ "wasm-bindgen", ] @@ -4504,6 +4504,7 @@ version = "1.2.160" dependencies = [ "anyhow", "console_error_panic_hook", + "js-sys", "once_cell", "parking_lot_core 0.9.1", "path-clean", @@ -4522,9 +4523,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" dependencies = [ "cfg-if 1.0.0", "serde", @@ -4534,9 +4535,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" dependencies = [ "bumpalo", "lazy_static", @@ -4561,9 +4562,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4571,9 +4572,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" dependencies = [ "proc-macro2", "quote", @@ -4584,9 +4585,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" +checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" [[package]] name = "wasmer" @@ -4864,9 +4865,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.55" +version = "0.3.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/crates/swc/src/config/mod.rs b/crates/swc/src/config/mod.rs index 9a524d68b50..320a2fa0ab6 100644 --- a/crates/swc/src/config/mod.rs +++ b/crates/swc/src/config/mod.rs @@ -466,7 +466,7 @@ impl Options { swc_plugin_runner::cache::init_plugin_module_cache_once(&experimental.cache_root); } - crate::plugin::plugins(plugin_resolver, experimental, plugin_context) + crate::plugin::plugins(Some(plugin_resolver), experimental, plugin_context) }; // Native runtime plugin target, based on assumption we have @@ -486,7 +486,8 @@ impl Options { env_name: self.env_name.to_owned(), }; - crate::plugin::plugins(experimental, plugin_context) + swc_plugin_runner::cache::init_plugin_module_cache_once(); + crate::plugin::plugins(None, experimental, plugin_context) }; #[cfg(not(feature = "plugin"))] diff --git a/crates/swc/src/plugin.rs b/crates/swc/src/plugin.rs index d411307b7e1..aed99ea45f3 100644 --- a/crates/swc/src/plugin.rs +++ b/crates/swc/src/plugin.rs @@ -41,9 +41,9 @@ pub struct PluginContext { pub env_name: String, } -#[cfg(all(feature = "plugin", not(target_arch = "wasm32")))] +#[cfg(feature = "plugin")] pub fn plugins( - resolver: CachingResolver, + resolver: Option>, config: crate::config::JscExperimental, plugin_context: PluginContext, ) -> impl Fold { @@ -56,18 +56,13 @@ pub fn plugins( } } -#[cfg(all(feature = "plugin", target_arch = "wasm32"))] -pub fn plugins(config: crate::config::JscExperimental, plugin_context: PluginContext) -> impl Fold { - swc_ecma_transforms::pass::noop() -} - #[cfg(not(feature = "plugin"))] pub fn plugins() -> impl Fold { noop() } struct RustPlugins { - resolver: CachingResolver, + resolver: Option>, plugins: Option>, plugin_context: PluginContext, } @@ -109,6 +104,8 @@ impl RustPlugins { let resolved_path = self .resolver + .as_ref() + .expect("filesystem_cache should provide resolver") .resolve(&FileName::Real(PathBuf::from(&p.0)), &p.0)?; let path = if let FileName::Real(value) = resolved_path { @@ -143,8 +140,36 @@ impl RustPlugins { #[cfg(all(feature = "plugin", target_arch = "wasm32"))] fn apply(&mut self, n: Program) -> Result { - //not implemented - Ok(n) + use std::{path::PathBuf, sync::Arc}; + + use anyhow::Context; + use swc_common::{plugin::Serialized, FileName}; + use swc_ecma_loader::resolve::Resolve; + + let mut serialized = Serialized::serialize(&n)?; + + if let Some(plugins) = &self.plugins { + for p in plugins { + let config_json = serde_json::to_string(&p.1) + .context("Failed to serialize plugin config as json") + .and_then(|value| Serialized::serialize(&value))?; + + let context_json = serde_json::to_string(&self.plugin_context) + .context("Failed to serialize plugin context as json") + .and_then(|value| Serialized::serialize(&value))?; + + serialized = swc_plugin_runner::apply_transform_plugin( + &p.0, + &PathBuf::from(&p.0), + &swc_plugin_runner::cache::PLUGIN_MODULE_CACHE, + serialized, + config_json, + context_json, + )?; + } + } + + Serialized::deserialize(&serialized) } } diff --git a/crates/swc_plugin_runner/src/cache.rs b/crates/swc_plugin_runner/src/cache.rs index 36de0772d3f..b2d71fe4949 100644 --- a/crates/swc_plugin_runner/src/cache.rs +++ b/crates/swc_plugin_runner/src/cache.rs @@ -92,16 +92,18 @@ fn create_filesystem_cache(filesystem_cache_root: &Option) -> Option) { - #[cfg(feature = "filesystem_cache")] PLUGIN_MODULE_CACHE.inner.get_or_init(|| { Mutex::new(CacheInner { fs_cache: create_filesystem_cache(filesystem_cache_root), loaded_module_bytes: Default::default(), }) }); +} - #[cfg(target_arch = "wasm32")] +#[cfg(feature = "memory_cache")] +pub fn init_plugin_module_cache_once() { PLUGIN_MODULE_CACHE.inner.get_or_init(|| { Mutex::new(CacheInner { loaded_module_bytes: Default::default(), @@ -185,22 +187,27 @@ impl PluginModuleCache { Ok(module) } - #[cfg(target_arch = "wasm32")] + #[cfg(feature = "memory_cache")] pub fn load_module(&self, binary_path: &Path) -> Result { let binary_path = binary_path.to_path_buf(); let mut inner_cache = self.inner.get().expect("Cache should be available").lock(); - // if constructed Module is available in-memory, directly return it. - // Note we do not invalidate in-memory cache currently: if wasm binary is - // replaced in-process lifecycle (i.e devserver) it won't be reflected. - let in_memory_module_bytes = inner_cache.loaded_module_bytes.get(&binary_path); - if let Some(module) = in_memory_module_bytes { - //TODO: In native runtime we have to reconstruct module using raw bytes in - // memory cache. requires https://github.com/wasmerio/wasmer/pull/2821 - unimplemented!("Not implemented yet"); - } + // In case of memory_cache, we do not have way to resolve / load modules + // externally. Bail out if cache doesn't have corresponding binary. + let in_memory_module_bytes = inner_cache + .loaded_module_bytes + .get(&binary_path) + .ok_or_else(|| { + anyhow::anyhow!("Could not locate plugin binary {}", binary_path.display()) + })?; - unimplemented!("Not implemented yet"); + //TODO: In native runtime we have to reconstruct module using raw bytes in + // memory cache. requires https://github.com/wasmerio/wasmer/pull/2821 + + let wasmer_store = Store::default(); + let module = Module::new(&wasmer_store, in_memory_module_bytes)?; + + Ok(module) } /// An experimental interface to store externally loaded module bytes into @@ -212,7 +219,7 @@ impl PluginModuleCache { /// /// This interface is not a public, but also will likely change anytime /// while stablizing plugin interface. - #[cfg(target_arch = "wasm32")] + #[cfg(feature = "memory_cache")] pub fn store_once(&self, module_name: &str, module_bytes: Vec) { // We use path as canonical id for the cache let binary_path = PathBuf::from(module_name); diff --git a/crates/swc_plugin_runner/src/lib.rs b/crates/swc_plugin_runner/src/lib.rs index 997921599d8..521c402d647 100644 --- a/crates/swc_plugin_runner/src/lib.rs +++ b/crates/swc_plugin_runner/src/lib.rs @@ -13,7 +13,6 @@ mod memory_interop; mod transform_executor; // entrypoint fn swc calls to perform its transform via plugin. -#[cfg(not(target_arch = "wasm32"))] pub fn apply_transform_plugin( plugin_name: &str, path: &Path, @@ -35,8 +34,3 @@ pub fn apply_transform_plugin( ) }) } - -#[cfg(target_arch = "wasm32")] -pub fn apply_transform_plugin() -> Result { - unimplemented!("Not implemented yet"); -} diff --git a/crates/swc_plugin_runner/src/load_plugin.rs b/crates/swc_plugin_runner/src/load_plugin.rs index ad05c4aec38..107bbced41b 100644 --- a/crates/swc_plugin_runner/src/load_plugin.rs +++ b/crates/swc_plugin_runner/src/load_plugin.rs @@ -15,7 +15,6 @@ use crate::{ }, }; -#[cfg(feature = "filesystem_cache")] #[tracing::instrument(level = "info", skip_all)] pub fn load_plugin( plugin_path: &std::path::Path, @@ -99,6 +98,8 @@ pub fn load_plugin( // Plugin binary can be either wasm32-wasi or wasm32-unknown-unknown. // Wasi specific env need to be initialized if given module targets wasm32-wasi. + // TODO: wasm host native runtime throws 'Memory should be set on `WasiEnv` + // first' let instance = if is_wasi_module(&module) { // Create the `WasiEnv`. let mut wasi_env = WasiState::new( @@ -124,11 +125,3 @@ pub fn load_plugin( Err(err) => Err(err), }; } - -#[cfg(not(feature = "filesystem_cache"))] -pub fn load_plugin( - plugin_path: &std::path::Path, - cache: &once_cell::sync::Lazy, -) -> Result<(Instance, Arc>>), Error> { - unimplemented!("not implemented"); -} diff --git a/crates/wasm/Cargo.toml b/crates/wasm/Cargo.toml index 7a7db526978..d4ed5402397 100644 --- a/crates/wasm/Cargo.toml +++ b/crates/wasm/Cargo.toml @@ -23,24 +23,26 @@ plugin = [ "wasmer-wasi", "wasmer/js-default", "wasmer-wasi/js-default", + "js-sys", ] [dependencies] anyhow = "1.0.42" console_error_panic_hook = "0.1.6" +js-sys = { version = "0.3.56", optional = true } once_cell = "1.10.0" parking_lot_core = "0.9.1" path-clean = "0.1" -serde = {version = "1", features = ["derive"]} +serde = { version = "1", features = ["derive"] } serde_json = "1" -swc = {path = "../swc"} -swc_common = {path = "../swc_common"} -swc_ecma_lints = {path = "../swc_ecma_lints", features = [ +swc = { path = "../swc" } +swc_common = { path = "../swc_common" } +swc_ecma_lints = { path = "../swc_ecma_lints", features = [ "non_critical_lints", -]} -swc_ecmascript = {path = "../swc_ecmascript"} -swc_plugin_runner = {path = "../swc_plugin_runner", default-features = false, optional = true} -tracing = {version = "0.1.32", features = ["release_max_level_off"]} -wasm-bindgen = {version = "0.2", features = ["serde-serialize"]} -wasmer = {version = "2.2.1", optional = true, default-features = false} -wasmer-wasi = {version = "2.2.1", optional = true, default-features = false} +] } +swc_ecmascript = { path = "../swc_ecmascript" } +swc_plugin_runner = { path = "../swc_plugin_runner", default-features = false, optional = true } +tracing = { version = "0.1.32", features = ["release_max_level_off"] } +wasm-bindgen = { version = "0.2", features = ["serde-serialize"] } +wasmer = { version = "2.2.1", optional = true, default-features = false } +wasmer-wasi = { version = "2.2.1", optional = true, default-features = false } diff --git a/crates/wasm/src/lib.rs b/crates/wasm/src/lib.rs index 52b72116743..e0260422348 100644 --- a/crates/wasm/src/lib.rs +++ b/crates/wasm/src/lib.rs @@ -8,7 +8,7 @@ use swc::{ }; use swc_common::{comments::Comments, FileName, FilePathMapping, SourceMap}; use swc_ecmascript::ast::{EsVersion, Program}; -use wasm_bindgen::prelude::*; +use wasm_bindgen::{prelude::*, JsCast}; fn convert_err(err: Error) -> JsValue { format!("{:?}", err).into() @@ -154,23 +154,46 @@ pub fn transform_sync( #[cfg(feature = "plugin")] { - // TODO: This is probably very inefficient, including each transform - // deserializes plugin bytes. - let plugin_bytes = if experimental_plugin_bytes_resolver.is_object() { - JsValue::into_serde::>>( - &experimental_plugin_bytes_resolver, - ) - .expect("Object should be available") - } else { - Default::default() - }; + if experimental_plugin_bytes_resolver.is_object() { + use js_sys::{Array, Object, Uint8Array}; - // In here we 'inject' externally loaded bytes into the cache, so remaining - // plugin_runner execution path works as much as similar between embedded - // runtime. - plugin_bytes.into_iter().for_each(|(key, bytes)| { - swc_plugin_runner::cache::PLUGIN_MODULE_CACHE.store_once(&key, bytes.clone()) - }); + // TODO: This is probably very inefficient, including each transform + // deserializes plugin bytes. + let plugin_bytes_resolver_object: Object = experimental_plugin_bytes_resolver + .try_into() + .expect("Resolver should be a js object"); + + swc_plugin_runner::cache::init_plugin_module_cache_once(); + + let entries = Object::entries(&plugin_bytes_resolver_object); + for entry in entries.iter() { + let entry: Array = entry + .try_into() + .expect("Resolver object missing either key or value"); + let name: String = entry + .get(0) + .as_string() + .expect("Resolver key should be a string"); + let buffer = entry.get(1); + + //https://github.com/rustwasm/wasm-bindgen/issues/2017#issue-573013044 + //We may use https://github.com/cloudflare/serde-wasm-bindgen instead later + let data = if JsCast::is_instance_of::(&buffer) { + JsValue::from(Array::from(&buffer)) + } else { + buffer + }; + + let bytes: Vec = data + .into_serde() + .expect("Could not read byte from plugin resolver"); + + // In here we 'inject' externally loaded bytes into the cache, so + // remaining plugin_runner execution path works as much as + // similar between embedded runtime. + swc_plugin_runner::cache::PLUGIN_MODULE_CACHE.store_once(&name, bytes); + } + } } try_with_handler( diff --git a/tests/rust-plugins/swc_internal_plugin/Cargo.lock b/tests/rust-plugins/swc_internal_plugin/Cargo.lock index 1eed798e70a..2fa0dd9c6ff 100644 --- a/tests/rust-plugins/swc_internal_plugin/Cargo.lock +++ b/tests/rust-plugins/swc_internal_plugin/Cargo.lock @@ -768,7 +768,7 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "swc_atoms" -version = "0.2.9" +version = "0.2.10" dependencies = [ "string_cache", "string_cache_codegen", @@ -802,7 +802,7 @@ dependencies = [ [[package]] name = "swc_ecma_ast" -version = "0.71.0" +version = "0.73.0" dependencies = [ "is-macro", "num-bigint", @@ -816,7 +816,7 @@ dependencies = [ [[package]] name = "swc_ecma_parser" -version = "0.95.2" +version = "0.97.2" dependencies = [ "either", "enum_kind", @@ -834,7 +834,7 @@ dependencies = [ [[package]] name = "swc_ecma_quote" -version = "0.6.0" +version = "0.8.0" dependencies = [ "swc_atoms", "swc_common", @@ -845,7 +845,7 @@ dependencies = [ [[package]] name = "swc_ecma_quote_macros" -version = "0.5.0" +version = "0.7.0" dependencies = [ "anyhow", "pmutil", @@ -861,7 +861,7 @@ dependencies = [ [[package]] name = "swc_ecma_utils" -version = "0.74.0" +version = "0.76.0" dependencies = [ "indexmap", "once_cell", @@ -874,7 +874,7 @@ dependencies = [ [[package]] name = "swc_ecma_visit" -version = "0.57.0" +version = "0.59.0" dependencies = [ "num-bigint", "swc_atoms", @@ -914,7 +914,7 @@ dependencies = [ [[package]] name = "swc_plugin" -version = "0.35.0" +version = "0.37.0" dependencies = [ "swc_atoms", "swc_common",