@rspack/binding 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Cargo.toml ADDED
@@ -0,0 +1,44 @@
1
+ [package]
2
+ edition = "2021"
3
+ name = "rspack_node"
4
+ publish = false
5
+ version = "0.1.0"
6
+
7
+ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8
+
9
+ [lib]
10
+ crate-type = ["cdylib"]
11
+
12
+ [dependencies]
13
+ anyhow = {version = "1", features = ["backtrace"]}
14
+ async-trait = "0.1.53"
15
+ backtrace = "0.3"
16
+ dashmap = "5.0.0"
17
+ futures = "0.3"
18
+ napi = {git = "https://github.com/speedy-js/napi-rs", branch = "speedy-release", default-features = false, features = [
19
+ "async",
20
+ "tokio_rt",
21
+ "serde-json",
22
+ ]}
23
+ napi-derive = {git = "https://github.com/speedy-js/napi-rs", branch = "speedy-release"}
24
+ napi-sys = {git = "https://github.com/speedy-js/napi-rs", branch = "speedy-release"}
25
+ once_cell = "1"
26
+ regex = "1.6.0"
27
+ rspack = {path = "../rspack"}
28
+ rspack_binding_options = {path = "../rspack_binding_options", features = ["node-api"]}
29
+ rspack_core = {path = "../rspack_core"}
30
+ rspack_error = {path = "../rspack_error"}
31
+ rspack_plugin_html = {path = "../rspack_plugin_html"}
32
+ serde = {version = "1", features = ["derive"]}
33
+ serde_json = "1"
34
+ tokio = {version = "1.17.0", features = ["full"]}
35
+ tracing = "0.1.34"
36
+
37
+ [target.'cfg(all(not(all(target_os = "linux", target_arch = "aarch64", target_env = "musl"))))'.dependencies]
38
+ mimalloc-rust = {version = "0.1"}
39
+
40
+ [build-dependencies]
41
+ napi-build = {git = "https://github.com/speedy-js/napi-rs", branch = "speedy-release"}
42
+
43
+ [dev-dependencies]
44
+ testing_macros = {version = "0.2.5"}
package/binding.js ADDED
@@ -0,0 +1,221 @@
1
+ const { existsSync, readFileSync } = require('fs')
2
+ const { join } = require('path')
3
+
4
+ const { platform, arch } = process
5
+
6
+ let nativeBinding = null
7
+ let localFileExisted = false
8
+ let loadError = null
9
+
10
+ function isMusl() {
11
+ // For Node 10
12
+ if (!process.report || typeof process.report.getReport !== 'function') {
13
+ try {
14
+ return readFileSync('/usr/bin/ldd', 'utf8').includes('musl')
15
+ } catch (e) {
16
+ return true
17
+ }
18
+ } else {
19
+ const { glibcVersionRuntime } = process.report.getReport().header
20
+ return !glibcVersionRuntime
21
+ }
22
+ }
23
+
24
+ switch (platform) {
25
+ case 'android':
26
+ switch (arch) {
27
+ case 'arm64':
28
+ localFileExisted = existsSync(join(__dirname, 'rspack.android-arm64.node'))
29
+ try {
30
+ if (localFileExisted) {
31
+ nativeBinding = require('./rspack.android-arm64.node')
32
+ } else {
33
+ nativeBinding = require('@rspack/binding-android-arm64')
34
+ }
35
+ } catch (e) {
36
+ loadError = e
37
+ }
38
+ break
39
+ case 'arm':
40
+ localFileExisted = existsSync(join(__dirname, 'rspack.android-arm-eabi.node'))
41
+ try {
42
+ if (localFileExisted) {
43
+ nativeBinding = require('./rspack.android-arm-eabi.node')
44
+ } else {
45
+ nativeBinding = require('@rspack/binding-android-arm-eabi')
46
+ }
47
+ } catch (e) {
48
+ loadError = e
49
+ }
50
+ break
51
+ default:
52
+ throw new Error(`Unsupported architecture on Android ${arch}`)
53
+ }
54
+ break
55
+ case 'win32':
56
+ switch (arch) {
57
+ case 'x64':
58
+ localFileExisted = existsSync(join(__dirname, 'rspack.win32-x64-msvc.node'))
59
+ try {
60
+ if (localFileExisted) {
61
+ nativeBinding = require('./rspack.win32-x64-msvc.node')
62
+ } else {
63
+ nativeBinding = require('@rspack/binding-win32-x64-msvc')
64
+ }
65
+ } catch (e) {
66
+ loadError = e
67
+ }
68
+ break
69
+ case 'ia32':
70
+ localFileExisted = existsSync(join(__dirname, 'rspack.win32-ia32-msvc.node'))
71
+ try {
72
+ if (localFileExisted) {
73
+ nativeBinding = require('./rspack.win32-ia32-msvc.node')
74
+ } else {
75
+ nativeBinding = require('@rspack/binding-win32-ia32-msvc')
76
+ }
77
+ } catch (e) {
78
+ loadError = e
79
+ }
80
+ break
81
+ case 'arm64':
82
+ localFileExisted = existsSync(join(__dirname, 'rspack.win32-arm64-msvc.node'))
83
+ try {
84
+ if (localFileExisted) {
85
+ nativeBinding = require('./rspack.win32-arm64-msvc.node')
86
+ } else {
87
+ nativeBinding = require('@rspack/binding-win32-arm64-msvc')
88
+ }
89
+ } catch (e) {
90
+ loadError = e
91
+ }
92
+ break
93
+ default:
94
+ throw new Error(`Unsupported architecture on Windows: ${arch}`)
95
+ }
96
+ break
97
+ case 'darwin':
98
+ switch (arch) {
99
+ case 'x64':
100
+ localFileExisted = existsSync(join(__dirname, 'rspack.darwin-x64.node'))
101
+ try {
102
+ if (localFileExisted) {
103
+ nativeBinding = require('./rspack.darwin-x64.node')
104
+ } else {
105
+ nativeBinding = require('@rspack/binding-darwin-x64')
106
+ }
107
+ } catch (e) {
108
+ loadError = e
109
+ }
110
+ break
111
+ case 'arm64':
112
+ localFileExisted = existsSync(join(__dirname, 'rspack.darwin-arm64.node'))
113
+ try {
114
+ if (localFileExisted) {
115
+ nativeBinding = require('./rspack.darwin-arm64.node')
116
+ } else {
117
+ nativeBinding = require('@rspack/binding-darwin-arm64')
118
+ }
119
+ } catch (e) {
120
+ loadError = e
121
+ }
122
+ break
123
+ default:
124
+ throw new Error(`Unsupported architecture on macOS: ${arch}`)
125
+ }
126
+ break
127
+ case 'freebsd':
128
+ if (arch !== 'x64') {
129
+ throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
130
+ }
131
+ localFileExisted = existsSync(join(__dirname, 'rspack.freebsd-x64.node'))
132
+ try {
133
+ if (localFileExisted) {
134
+ nativeBinding = require('./rspack.freebsd-x64.node')
135
+ } else {
136
+ nativeBinding = require('@rspack/binding-freebsd-x64')
137
+ }
138
+ } catch (e) {
139
+ loadError = e
140
+ }
141
+ break
142
+ case 'linux':
143
+ switch (arch) {
144
+ case 'x64':
145
+ if (isMusl()) {
146
+ localFileExisted = existsSync(join(__dirname, 'rspack.linux-x64-musl.node'))
147
+ try {
148
+ if (localFileExisted) {
149
+ nativeBinding = require('./rspack.linux-x64-musl.node')
150
+ } else {
151
+ nativeBinding = require('@rspack/binding-linux-x64-musl')
152
+ }
153
+ } catch (e) {
154
+ loadError = e
155
+ }
156
+ } else {
157
+ localFileExisted = existsSync(join(__dirname, 'rspack.linux-x64-gnu.node'))
158
+ try {
159
+ if (localFileExisted) {
160
+ nativeBinding = require('./rspack.linux-x64-gnu.node')
161
+ } else {
162
+ nativeBinding = require('@rspack/binding-linux-x64-gnu')
163
+ }
164
+ } catch (e) {
165
+ loadError = e
166
+ }
167
+ }
168
+ break
169
+ case 'arm64':
170
+ if (isMusl()) {
171
+ localFileExisted = existsSync(join(__dirname, 'rspack.linux-arm64-musl.node'))
172
+ try {
173
+ if (localFileExisted) {
174
+ nativeBinding = require('./rspack.linux-arm64-musl.node')
175
+ } else {
176
+ nativeBinding = require('@rspack/binding-linux-arm64-musl')
177
+ }
178
+ } catch (e) {
179
+ loadError = e
180
+ }
181
+ } else {
182
+ localFileExisted = existsSync(join(__dirname, 'rspack.linux-arm64-gnu.node'))
183
+ try {
184
+ if (localFileExisted) {
185
+ nativeBinding = require('./rspack.linux-arm64-gnu.node')
186
+ } else {
187
+ nativeBinding = require('@rspack/binding-linux-arm64-gnu')
188
+ }
189
+ } catch (e) {
190
+ loadError = e
191
+ }
192
+ }
193
+ break
194
+ case 'arm':
195
+ localFileExisted = existsSync(join(__dirname, 'rspack.linux-arm-gnueabihf.node'))
196
+ try {
197
+ if (localFileExisted) {
198
+ nativeBinding = require('./rspack.linux-arm-gnueabihf.node')
199
+ } else {
200
+ nativeBinding = require('@rspack/binding-linux-arm-gnueabihf')
201
+ }
202
+ } catch (e) {
203
+ loadError = e
204
+ }
205
+ break
206
+ default:
207
+ throw new Error(`Unsupported architecture on Linux: ${arch}`)
208
+ }
209
+ break
210
+ default:
211
+ throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
212
+ }
213
+
214
+ if (!nativeBinding) {
215
+ if (loadError) {
216
+ throw loadError
217
+ }
218
+ throw new Error(`Failed to load native binding`)
219
+ }
220
+
221
+ module.exports.default = module.exports = nativeBinding
package/build.rs ADDED
@@ -0,0 +1,5 @@
1
+ extern crate napi_build;
2
+
3
+ fn main() {
4
+ napi_build::setup();
5
+ }
package/napirs.rc.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "napi": {
3
+ "name": "rspack",
4
+ "triples": {
5
+ "defaults": true,
6
+ "additional": [
7
+ "x86_64-unknown-linux-musl",
8
+ "aarch64-unknown-linux-gnu",
9
+ "i686-pc-windows-msvc",
10
+ "armv7-unknown-linux-gnueabihf",
11
+ "aarch64-apple-darwin",
12
+ "x86_64-unknown-freebsd",
13
+ "aarch64-unknown-linux-musl",
14
+ "aarch64-pc-windows-msvc",
15
+ "aarch64-linux-android",
16
+ "armv7-linux-androideabi"
17
+ ]
18
+ }
19
+ }
20
+ }
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@rspack/binding",
3
+ "version": "0.0.2",
4
+ "description": "Node binding for rspack",
5
+ "main": "binding.js",
6
+ "types": "binding.d.ts",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "keywords": [],
11
+ "author": "",
12
+ "license": "ISC",
13
+ "devDependencies": {
14
+ "@napi-rs/cli": "^2.6.2",
15
+ "why-is-node-running": "2.2.1"
16
+ },
17
+ "scripts": {
18
+ "build": "napi build --platform --js false --dts binding.d.ts --config napirs.rc.json",
19
+ "build:release": "napi build --release --platform --js false --dts binding.d.ts --config napirs.rc.json"
20
+ }
21
+ }
@@ -0,0 +1,11 @@
1
+ use std::sync::Arc;
2
+
3
+ use dashmap::DashMap;
4
+ use napi::threadsafe_function::{ErrorStrategy, ThreadsafeFunction};
5
+ use once_cell::sync::Lazy;
6
+ use tokio::sync::oneshot::Sender;
7
+
8
+ pub type ThreadsafeRspackCallback = ThreadsafeFunction<String, ErrorStrategy::CalleeHandled>;
9
+
10
+ pub static REGISTERED_DONE_SENDERS: Lazy<Arc<DashMap<usize, Sender<()>>>> =
11
+ Lazy::new(Default::default);
@@ -0,0 +1,124 @@
1
+ use std::fmt::Debug;
2
+ use std::sync::atomic::{AtomicUsize, Ordering};
3
+
4
+ use rspack_core::{Plugin, PluginBuildEndHookOutput};
5
+
6
+ use anyhow::Context;
7
+ use async_trait::async_trait;
8
+ use napi::threadsafe_function::ThreadsafeFunctionCallMode;
9
+ use napi::Error;
10
+ use napi_derive::napi;
11
+ use once_cell::sync::Lazy;
12
+ use serde::{Deserialize, Serialize};
13
+ use tokio::sync::oneshot;
14
+
15
+ mod common;
16
+ pub mod utils;
17
+ pub use utils::create_node_adapter_from_plugin_callbacks;
18
+
19
+ use common::ThreadsafeRspackCallback;
20
+ use common::REGISTERED_DONE_SENDERS;
21
+
22
+ pub static CALL_ID: Lazy<AtomicUsize> = Lazy::new(|| AtomicUsize::new(1));
23
+
24
+ pub struct RspackPluginNodeAdapter {
25
+ pub done_tsfn: ThreadsafeRspackCallback,
26
+ }
27
+
28
+ #[derive(Serialize, Deserialize, Debug)]
29
+ #[serde(rename_all = "camelCase")]
30
+ pub struct RspackThreadsafeContext<T: Debug> {
31
+ id: usize,
32
+ inner: T,
33
+ }
34
+
35
+ impl<T: Debug> RspackThreadsafeContext<T> {
36
+ pub fn new(payload: T) -> Self {
37
+ Self {
38
+ id: CALL_ID.fetch_add(1, Ordering::SeqCst),
39
+ inner: payload,
40
+ }
41
+ }
42
+
43
+ #[inline(always)]
44
+ pub fn get_call_id(&self) -> usize {
45
+ self.id
46
+ }
47
+ }
48
+
49
+ #[derive(Serialize, Deserialize, Debug)]
50
+ #[serde(rename_all = "camelCase")]
51
+ pub struct RspackThreadsafeResult<T: Debug> {
52
+ id: usize,
53
+ inner: T,
54
+ }
55
+
56
+ impl<T: Debug> RspackThreadsafeResult<T> {
57
+ #[inline(always)]
58
+ pub fn get_call_id(&self) -> usize {
59
+ self.id
60
+ }
61
+ }
62
+
63
+ #[derive(Serialize, Deserialize, Debug)]
64
+ #[napi(object)]
65
+ pub struct OnLoadContext {
66
+ pub id: String,
67
+ }
68
+
69
+ impl Debug for RspackPluginNodeAdapter {
70
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71
+ f.debug_struct("RspackPluginNodeAdapter").finish()
72
+ }
73
+ }
74
+
75
+ #[async_trait]
76
+ impl Plugin for RspackPluginNodeAdapter {
77
+ fn name(&self) -> &'static str {
78
+ "rspack_plugin_node_adapter"
79
+ }
80
+ #[tracing::instrument(skip_all)]
81
+ async fn done(&self) -> PluginBuildEndHookOutput {
82
+ let context = RspackThreadsafeContext::new(());
83
+
84
+ let (tx, rx) = oneshot::channel::<()>();
85
+
86
+ match REGISTERED_DONE_SENDERS.entry(context.get_call_id()) {
87
+ dashmap::mapref::entry::Entry::Vacant(v) => {
88
+ v.insert(tx);
89
+ }
90
+ dashmap::mapref::entry::Entry::Occupied(_) => {
91
+ let err = Error::new(
92
+ napi::Status::Unknown,
93
+ format!(
94
+ "duplicated call id encountered {}, please file an issue.",
95
+ context.get_call_id(),
96
+ ),
97
+ );
98
+ self
99
+ .done_tsfn
100
+ .call(Err(err.clone()), ThreadsafeFunctionCallMode::Blocking);
101
+
102
+ let any_error = anyhow::Error::from(err);
103
+ return Err(any_error.into());
104
+ }
105
+ }
106
+
107
+ let value = serde_json::to_string(&context).map_err(|_| {
108
+ Error::new(
109
+ napi::Status::Unknown,
110
+ "unable to convert context".to_owned(),
111
+ )
112
+ });
113
+
114
+ self
115
+ .done_tsfn
116
+ .call(value, ThreadsafeFunctionCallMode::Blocking);
117
+
118
+ let t = rx
119
+ .await
120
+ .context("failed to receive done result")
121
+ .map_err(|err| err.into());
122
+ return t;
123
+ }
124
+ }
@@ -0,0 +1,62 @@
1
+ use napi::bindgen_prelude::*;
2
+ use napi::{
3
+ threadsafe_function::{ErrorStrategy, ThreadSafeResultContext, ThreadsafeFunction},
4
+ Env,
5
+ };
6
+
7
+ use super::common::REGISTERED_DONE_SENDERS;
8
+ use crate::PluginCallbacks;
9
+
10
+ pub fn create_node_adapter_from_plugin_callbacks(
11
+ env: &Env,
12
+ plugin_callbacks: Option<PluginCallbacks>,
13
+ ) -> Result<Option<super::RspackPluginNodeAdapter>> {
14
+ plugin_callbacks
15
+ .map(|PluginCallbacks { done_callback }| {
16
+ let mut done_tsfn: ThreadsafeFunction<String, ErrorStrategy::CalleeHandled> = done_callback
17
+ .create_threadsafe_function(
18
+ 0,
19
+ |ctx| ctx.env.create_string_from_std(ctx.value).map(|v| vec![v]),
20
+ |ctx: ThreadSafeResultContext<Promise<String>>| {
21
+ let return_value = ctx.return_value;
22
+
23
+ ctx
24
+ .env
25
+ .execute_tokio_future(
26
+ async move {
27
+ let result = return_value.await?;
28
+
29
+ let result: super::RspackThreadsafeResult<()> =
30
+ serde_json::from_str(&result).expect("failed to evaluate done result");
31
+
32
+ tracing::debug!("build end result {:?}", result);
33
+
34
+ let sender = REGISTERED_DONE_SENDERS.remove(&result.get_call_id());
35
+
36
+ if let Some((_, sender)) = sender {
37
+ sender.send(()).map_err(|m| {
38
+ Error::new(
39
+ napi::Status::GenericFailure,
40
+ format!("failed to send result {:#?}", m),
41
+ )
42
+ })?;
43
+ } else {
44
+ return Err(Error::new(
45
+ napi::Status::GenericFailure,
46
+ "failed to get sender for plugin".to_owned(),
47
+ ));
48
+ }
49
+
50
+ Ok(())
51
+ },
52
+ |_, ret| Ok(ret),
53
+ )
54
+ .expect("failed to execute tokio future");
55
+ },
56
+ )?;
57
+ done_tsfn.unref(env)?;
58
+
59
+ Ok(super::RspackPluginNodeAdapter { done_tsfn })
60
+ })
61
+ .transpose()
62
+ }