This project uses the v7 of Node N-API. Please see following compatibility matrix.
Following OS are supported: * Linux * MacOs * Windows
Writing native node-js requires lots of boilerplate code. Node-bindgen generates external "C" glue code from rust code, including native module registration. node-bindgen make it writing node-js module easy and fun.
Install nj-cli command line, which will be used to generate the native library.
cargo install nj-cli
This is a one time step.
Add two dependencies to your projects' Cargo.toml
.
Add node-bindgen
as a regular dependency (as below):
[dependencies]
node-bindgen = { version = "5.1" }
Then add node-bindgen
's procedure macro to your build-dependencies as below:
[build-dependencies]
node-bindgen = { version = "5.1", features = ["build"] }
Then update crate type to cdylib
to generate node.js compatible native module:
[lib]
crate-type = ["cdylib"]
Finally, add build.rs
at the top of the project with following content:
fn main() {
node_bindgen::build::configure();
}
Here is a function that adds two numbers. Note that you don't need to worry about JS conversion.
```rust
use nodebindgen::derive::nodebindgen;
/// add two integer
fn sum(first: i32, second: i32) -> i32 { first + second }
```
To build node.js library, using nj-cli
to build:
nj-cli build
This will generate Node.js module in "./dist" folder.
To build a release version:
nj-cli build --release
./src
for ChangesWhile developing your native module, you may want to watch for file changes and run a command when a change occurs, for example cargo check
or cargo build
.
For this, we can use nj-cli watch
.
nj-cli watch
installs [if it does not exist] and passes arguments to cargo watch
. By default, nj-cli watch
will run cargo check
against your ./src
files.
To see all available methods for nj-cli watch
, run the following command:
nj-cli watch -- --help
Then in the Node.js, rust function can be invoked as normal node.js function:
```js $ node Welcome to Node.js v14.0.0. Type ".help" for more information.
let addon = require('./dist'); undefined addon.sum(2,3) 5
```
```rust
fn mul(first: i32,second: i32) -> i32 { first * second } ```
Rust function mul is re-mapped as multiply
Argument can be skipped if it is marked as optional ```rust
fn sum(first: i32, second: Option
Then sum can be invoked as
sum(10)or
sum(10,20)```
JS callback are mapped as Rust closure.
```rust
fn hello }
``` from node: ```js
let addon = require('./dist'); addon.hello(2,function(msg){
assert.equal(msg,"argument is: 2");
console.log(msg); // print out argument is 2
});
``` Callback are supported in Async rust as well. Async rust function is mapped to Node.js promise. ```rust use std::time::Duration;
use flvfutureaio::time::sleep;
use nodebindgen::derive::nodebindgen; async fn hello(arg: f64) -> f64 {
println!("sleeping");
sleep(Duration::from_secs(1)).await;
println!("woke and adding 10.0");
arg + 10.0
}
``` ```js
let addon = require('./dist'); addon.hello(5).then((val) => {
console.log("future value is %s",val);
}); ``` Structs, including generic structs, can have have the to-JS conversion boilerplate autogenerated.
Just apply the ```rust struct MyJson {
somename: String,
anumber: i64
} fn myjson() -> MyJson {
MyJson {
somename: "John".toowned(),
anumber: 1337
}
}
``` Note that the fields must implement
Enums will also have their JS representation autogenerated with the help of ```rust enum ErrorType {
WithMessage(String, usize),
WithFields {
val: usize
},
UnitErrorType
} fn withmessage() -> ErrorType {
ErrorType::WithMessage("test".toowned(), 321)
} fn with_fields() -> ErrorType {
ErrorType::WithFields {
val: 123
}
} fn with_unit() -> ErrorType {
ErrorType::UnitErrorType
}
``` Tuple variants will be converted into lists, struct variants converted to objects, and unit variants converted into strings matching the variant's name in PascalCase.
Generics and references are supported, with the same caveats as for structs. JavaScript class is supported. ```rust struct MyClass {
val: f64,
} impl MyClass { }
``` ```js
let addon = require('./dist');
const assert = require('assert'); let obj = new addon.MyObject(10);
assert.equal(obj.value,10,"verify value works");
assert.equal(obj.plusOne(),11);
``` There are more features in the examples folder. When using node-bindgen with electron on Windows, If your machine does not have a valid C/C++ compiler, install Microsoft VSCode. In the future, this file will be re-written in Rust, removing this dependency. Just make sure that you are compiling the rust module using
otherwise you will get dreaded If you'd like to contribute to the project, please read our Contributing guide. This project is licensed under the Apache license.let msg = format!("argument is: {}", first);
second(msg);
Support for Async Rust
[node_bindgen]
Struct serialization
node_bindgen
macro to your struct:[node_bindgen]
[node_bindgen]
js
let addon = require('./dist');
assert.deepStrictEqual(addon.my_json(), {
someName: "John",
aNumber: 1337
});
node_bindgen::core::TryIntoJs
themselves.
Any references must also implement Clone
.
Field names will be converted to camelCase.Enums
node_bindgen
:[node_bindgen]
[node_bindgen]
[node_bindgen]
[node_bindgen]
js
assert.deepStrictEqual(addon.withMessage(), {
withMessage: ["test", 321n]
});
assert.deepStrictEqual(addon.withFields(), {
withFields: {
val: 123n
}
});
assert.deepStrictEqual(addon.withUnit(), "UnitErrorType")
JavaScript class
[node_bindgen]
#[node_bindgen(constructor)]
fn new(val: f64) -> Self {
Self { val }
}
#[node_bindgen]
fn plus_one(&self) -> f64 {
self.val + 1.0
}
#[node_bindgen(getter)]
fn value(&self) -> f64 {
self.val
}
Windows + Electron Support
nj-build
must
compile a C++ file, win_delay_load_hook.cc
, and therefore it is required that the development
environment has a valid C/C++ compiler.
$ npx electron-build-env nj-cli build --release
A dynamic link library (DLL) initialization routine failed
when importing the rust module in electronContributing
License