-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add (experimental) nodejs interop crates (#9974)
**Description:** I'm experimenting with various options.
- Loading branch information
Showing
8 changed files
with
223 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
[package] | ||
authors = ["강동윤 <kdy1997.dev@gmail.com>"] | ||
description = "General interop for Babel" | ||
documentation = "https://rustdoc.swc.rs/swc_interop_babel/" | ||
edition = { workspace = true } | ||
license = { workspace = true } | ||
name = "swc_interop_babel" | ||
repository = { workspace = true } | ||
version = "0.1.0" | ||
|
||
|
||
[dependencies] | ||
napi = { workspace = true, features = ["napi4"] } | ||
serde = { workspace = true, features = ["derive"] } | ||
serde_json = { workspace = true } | ||
|
||
swc_interop_nodejs = { version = "0.1.0", path = "../swc_interop_nodejs" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod transform; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
use napi::JsFunction; | ||
use serde::{Deserialize, Serialize}; | ||
use swc_interop_nodejs::{js_hook::JsHook, types::AsJsonString}; | ||
|
||
pub struct JsTrasnform { | ||
f: JsHook<AsJsonString<TransformOutput>, AsJsonString<TransformOutput>>, | ||
} | ||
|
||
#[derive(Debug, Serialize, Deserialize)] | ||
pub struct TransformOutput { | ||
pub code: String, | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub map: Option<String>, | ||
} | ||
|
||
impl JsTrasnform { | ||
pub fn new(env: &napi::Env, f: &JsFunction) -> napi::Result<Self> { | ||
Ok(Self { | ||
f: JsHook::new(env, f)?, | ||
}) | ||
} | ||
|
||
pub async fn transform(&self, input: TransformOutput) -> napi::Result<TransformOutput> { | ||
Ok(self.f.call(AsJsonString(input)).await?.0) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
[package] | ||
authors = ["강동윤 <kdy1997.dev@gmail.com>"] | ||
description = "General interop for Node.js" | ||
documentation = "https://rustdoc.swc.rs/swc_interop_nodejs/" | ||
edition = { workspace = true } | ||
license = { workspace = true } | ||
name = "swc_interop_nodejs" | ||
repository = { workspace = true } | ||
version = "0.1.0" | ||
|
||
|
||
[dependencies] | ||
napi = { workspace = true, features = ["napi4", "tokio_rt", "serde-json"] } | ||
serde = { workspace = true } | ||
serde_json = { workspace = true } | ||
tokio = { workspace = true } | ||
tracing = { workspace = true } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
use std::marker::PhantomData; | ||
|
||
use napi::{ | ||
bindgen_prelude::Promise, | ||
threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunction}, | ||
JsFunction, | ||
}; | ||
use tracing::trace; | ||
|
||
use crate::types::{JsInput, JsOutput}; | ||
|
||
pub struct JsHook<I, O> | ||
where | ||
I: JsInput, | ||
O: JsOutput, | ||
{ | ||
f: ThreadsafeFunction<I>, | ||
_marker: PhantomData<O>, | ||
} | ||
|
||
impl<I, O> JsHook<I, O> | ||
where | ||
I: JsInput, | ||
O: JsOutput, | ||
{ | ||
pub fn new(env: &napi::Env, f: &JsFunction) -> napi::Result<Self> { | ||
Ok(Self { | ||
f: env.create_threadsafe_function(f, 0, |cx: ThreadSafeCallContext<I>| { | ||
let arg = cx.value.into_js(&cx.env)?.into_unknown(); | ||
|
||
if cfg!(debug_assertions) { | ||
trace!("Converted to js value"); | ||
} | ||
Ok(vec![arg]) | ||
})?, | ||
_marker: PhantomData, | ||
}) | ||
} | ||
|
||
#[tracing::instrument(skip_all, fields(perf = "JsHook::call"))] | ||
pub async fn call(&self, input: I) -> napi::Result<O> { | ||
if cfg!(debug_assertions) { | ||
trace!("Calling js function"); | ||
} | ||
|
||
let result: Promise<O> = self.f.call_async(Ok(input)).await?; | ||
|
||
let res = result.await?; | ||
|
||
Ok(res) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
pub mod js_hook; | ||
pub mod types; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
use std::fmt::Debug; | ||
|
||
use napi::{bindgen_prelude::FromNapiValue, Env, JsUnknown}; | ||
use serde::{de::DeserializeOwned, Serialize}; | ||
|
||
pub trait JsInput: 'static + Send + Debug { | ||
fn into_js(self, env: &Env) -> napi::Result<JsUnknown>; | ||
} | ||
|
||
impl JsInput for String { | ||
fn into_js(self, env: &Env) -> napi::Result<JsUnknown> { | ||
Ok(env.create_string(&self)?.into_unknown()) | ||
} | ||
} | ||
|
||
impl JsInput for Vec<String> { | ||
fn into_js(self, env: &Env) -> napi::Result<JsUnknown> { | ||
let mut arr = env.create_array_with_length(self.len())?; | ||
|
||
for (idx, s) in self.into_iter().enumerate() { | ||
arr.set_element(idx as _, s.into_js(env)?)?; | ||
} | ||
|
||
Ok(arr.into_unknown()) | ||
} | ||
} | ||
|
||
impl JsInput for Vec<u8> { | ||
fn into_js(self, env: &Env) -> napi::Result<JsUnknown> { | ||
Ok(env.create_buffer_with_data(self)?.into_unknown()) | ||
} | ||
} | ||
|
||
impl<A, B> JsInput for (A, B) | ||
where | ||
A: JsInput, | ||
B: JsInput, | ||
{ | ||
fn into_js(self, env: &Env) -> napi::Result<JsUnknown> { | ||
let mut arr = env.create_array(2)?; | ||
arr.set(0, self.0.into_js(env)?)?; | ||
arr.set(1, self.1.into_js(env)?)?; | ||
|
||
Ok(arr.coerce_to_object()?.into_unknown()) | ||
} | ||
} | ||
|
||
/// Seems like Vec<u8> is buggy | ||
pub trait JsOutput: 'static + FromNapiValue + Send + Debug {} | ||
|
||
impl JsOutput for String {} | ||
|
||
impl JsOutput for bool {} | ||
|
||
/// Note: This type stringifies the output json string, because it's faster. | ||
#[derive(Debug, Default)] | ||
pub struct AsJsonString<T>(pub T) | ||
where | ||
T: 'static + Send + Debug; | ||
|
||
impl<T> FromNapiValue for AsJsonString<T> | ||
where | ||
T: 'static + Send + Debug + DeserializeOwned, | ||
{ | ||
unsafe fn from_napi_value( | ||
env_raw: napi::sys::napi_env, | ||
napi_val: napi::sys::napi_value, | ||
) -> napi::Result<Self> { | ||
let env = Env::from_raw(env_raw); | ||
let json: String = env.from_js_value(JsUnknown::from_napi_value(env_raw, napi_val)?)?; | ||
let t = serde_json::from_str(&json)?; | ||
Ok(Self(t)) | ||
} | ||
} | ||
|
||
impl<T> JsOutput for AsJsonString<T> where T: 'static + Send + Debug + DeserializeOwned {} | ||
|
||
impl<T> JsInput for AsJsonString<T> | ||
where | ||
T: 'static + Send + Debug + Serialize, | ||
{ | ||
fn into_js(self, env: &napi::Env) -> napi::Result<napi::JsUnknown> { | ||
let json = serde_json::to_string(&self.0)?; | ||
Ok(env.create_string(json.as_str())?.into_unknown()) | ||
} | ||
} |