* Implement FromNapiValue trait as macro Code behavior should remain the same, with much less copy-pasting of the same code. Additionally, this commit adds a unit tests for test for FromNapiValue for tuple trait which appears to be currently missing. * Simplify macro arr_get Rewrite the macro to reduce the size of expanded code of the macro (from 4088 to 2429 lines). Although this is a minor change, this may help reduce the size of compiled library. * Refactor typegen Type::Path Removes the need for keeping mutable variable, as its only assigned once. * [API BREAKING] Update api for calling functions. This commit introduces new type FnArgs, that is created with the goal of distinguishing between calling JS function from rust with tuple as argument and calling with multiple arguments. Currently there is no possibility to add ToNapiValue value, as tuple is used to call callback functions with more than one argument from rust. With this change, callbacks with multiple arguments, should be called, with converting tuple of argument into FnArgs struct, either by calling into() on such tuple, or by creating FnArgs struct directly. * Add support for FnArgs when generating TS types Updated the macro for generating typescript types, to correctly handle FnArgs struct. This is now treated as passthrough type and generates the output from the type inside of FnArgs struct. * Implement ToNapiValue trait for tuple With the changes to callback calling API, it's now possible to safely add this change. This change allows to pass tuple from rust code to JS to match the existing FromNapiValue trait currently existing in the library.
napi-rs
This project was initialized from xray
A framework for building compiled Node.js add-ons in Rust via Node-API. Website: https://napi.rs
Platform Support
MSRV
Rust 1.80.0
| node12 | node14 | node16 | node18 | node20 | |
|---|---|---|---|---|---|
| Windows x64 | ✓ | ✓ | ✓ | ✓ | ✓ |
| Windows x86 | ✓ | ✓ | ✓ | ✓ | ✓ |
| Windows arm64 | ✓ | ✓ | ✓ | ✓ | ✓ |
| macOS x64 | ✓ | ✓ | ✓ | ✓ | ✓ |
| macOS aarch64 | ✓ | ✓ | ✓ | ✓ | ✓ |
| Linux x64 gnu | ✓ | ✓ | ✓ | ✓ | ✓ |
| Linux x64 musl | ✓ | ✓ | ✓ | ✓ | ✓ |
| Linux aarch64 gnu | ✓ | ✓ | ✓ | ✓ | ✓ |
| Linux aarch64 musl | ✓ | ✓ | ✓ | ✓ | ✓ |
| Linux arm gnueabihf | ✓ | ✓ | ✓ | ✓ | ✓ |
| Linux arm muslebihf | ✓ | ✓ | ✓ | ✓ | ✓ |
| Linux powerpc64le gnu | ✓ | ✓ | ✓ | ✓ | ✓ |
| Linux s390x gnu | ✓ | ✓ | ✓ | ✓ | ✓ |
| Linux riscv64 gnu | N/A | N/A | ✓ | ✓ | ✓ |
| Linux aarch64 android | ✓ | ✓ | ✓ | ✓ | ✓ |
| Linux armv7 android | ✓ | ✓ | ✓ | ✓ | ✓ |
| FreeBSD x64 | ✓ | ✓ | ✓ | ✓ | ✓ |
This library depends on Node-API and requires Node@10.0.0 or later.
We already have some packages written by napi-rs: node-rs
One nice feature is that this crate allows you to build add-ons purely with the Rust/JavaScript toolchain and without involving node-gyp.
Taste
You can start from package-template to play with
napi-rs
Define JavaScript functions
/// import the preludes
use napi::bindgen_prelude::*;
use napi_derive::napi;
/// module registration is done by the runtime, no need to explicitly do it now.
#[napi]
fn fibonacci(n: u32) -> u32 {
match n {
1 | 2 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
/// use `Fn`, `FnMut` or `FnOnce` traits to defined JavaScript callbacks
/// the return type of callbacks can only be `Result`.
#[napi]
fn get_cwd<T: Fn(String) -> Result<()>>(callback: T) {
callback(env::current_dir().unwrap().to_string_lossy().to_string()).unwrap();
}
/// or, define the callback signature in where clause
#[napi]
fn test_callback<T>(callback: T)
where T: Fn(String) -> Result<()>
{}
/// async fn, require `async` feature enabled.
/// [dependencies]
/// napi = {version="2", features=["async"]}
#[napi]
async fn read_file_async(path: String) -> Result<Buffer> {
tokio::fs::read(path)
.map(|r| match r {
Ok(content) => Ok(content.into()),
Err(e) => Err(Error::new(
Status::GenericFailure,
format!("failed to read file, {}", e),
)),
})
.await
}
more examples at examples
Building
This repository is a Cargo crate. Any napi-based add-on should contain Cargo.toml to make it a Cargo crate.
In your Cargo.toml you need to set the crate-type to "cdylib" so that cargo builds a C-style shared library that can be dynamically loaded by the Node executable. You'll also need to add this crate as a dependency.
[package]
name = "awesome"
[lib]
crate-type = ["cdylib"]
[dependencies]
napi = "3"
napi-derive = "3"
[build-dependencies]
napi-build = "1"
And create build.rs in your own project:
// build.rs
extern crate napi_build;
fn main() {
napi_build::setup();
}
So far, the napi build script has only been tested on macOS Linux Windows x64 MSVC and FreeBSD.
Install the @napi-rs/cli to help you build your Rust codes and copy Dynamic lib file to .node file in case you can require it in your program.
{
"package": "awesome-package",
"devDependencies": {
"@napi-rs/cli": "^1.0.0"
},
"napi": {
"name": "jarvis" // <----------- Config the name of native addon, or the napi command will use the name of `Cargo.toml` for the binary file name.
},
"scripts": {
"build": "napi build --release",
"build:debug": "napi build"
}
}
Then you can require your native binding:
require('./jarvis.node')
The module_name would be your package name in your Cargo.toml.
xxx => ./xxx.node
xxx-yyy => ./xxx_yyy.node
You can also copy Dynamic lib file to an appointed location:
napi build [--release] ./dll
napi build [--release] ./artifacts
There are documents which contains more details about the @napi-rs/cli usage.
Testing
Because libraries that depend on this crate must be loaded into a Node executable in order to resolve symbols, all tests are written in JavaScript in the test_module subdirectory.
To run tests:
yarn build:test
yarn test
Related projects
Features table
| Rust Type | Node Type | NAPI Version | Minimal Node version | Enable by napi feature |
|---|---|---|---|---|
| u32 | Number | 1 | v8.0.0 | |
| i32/i64 | Number | 1 | v8.0.0 | |
| f64 | Number | 1 | v8.0.0 | |
| bool | Boolean | 1 | v8.0.0 | |
| String/&'a str | String | 1 | v8.0.0 | |
| Latin1String | String | 1 | v8.0.0 | latin1 |
| UTF16String | String | 1 | v8.0.0 | |
| Object | Object | 1 | v8.0.0 | |
| serde_json::Map | Object | 1 | v8.0.0 | serde-json |
| serde_json::Value | any | 1 | v8.0.0 | serde-json |
| Array | Array | 1 | v8.0.0 | |
| Vec | Array | 1 | v8.0.0 | |
| Buffer | Buffer | 1 | v8.0.0 | |
| External | External | 1 | v8.0.0 | |
| Null | null | 1 | v8.0.0 | |
| Undefined/() | undefined | 1 | v8.0.0 | |
| Result<()> | Error | 1 | v8.0.0 | |
| T: Fn(...) -> Result | Function | 1 | v8.0.0 | |
| Async/Future | Promise | 4 | v10.6.0 | async |
| AsyncTask | Promise | 1 | v8.5.0 | |
| JsGlobal | global | 1 | v8.0.0 | |
| JsSymbol | Symbol | 1 | v8.0.0 | |
| Int8Array/Uint8Array ... | TypedArray | 1 | v8.0.0 | |
| JsFunction | threadsafe function | 4 | v10.6.0 | napi4 |
| BigInt | BigInt | 6 | v10.7.0 | napi6 |