NAPI-RS

A framework for building compiled Node.js add-ons in Rust via Node-API

README

napi-rs


This project was initialized from xray


A framework for building compiled Node.jsadd-ons in Rustvia Node-API. Website: https://napi.rs

MSRV


Rust1.57.0

| node12 | node14 | node16 | node18 |
| :--- | :--- | :--- | :--- |
| 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 aarch64 android| ✓| ✓| ✓| ✓ |
| Linux armv7 android| ✓| ✓| ✓| ✓ |
| FreeBSD x64| ✓| ✓| ✓| ✓ |

This library depends on Node-API and requires Node@10.0.0or 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/JavaScripttoolchain and without involving node-gyp.

Taste


You can start from package-template to play with napi-rs


Define JavaScript functions


  1. ``` rust
  2. /// import the preludes
  3. use napi::bindgen_prelude::*;
  4. use napi_derive::napi;

  5. /// module registration is done by the runtime, no need to explicitly do it now.
  6. #[napi]
  7. fn fibonacci(n: u32) -> u32 {
  8.   match n {
  9.     1 | 2 => 1,
  10.     _ => fibonacci(n - 1) + fibonacci(n - 2),
  11.   }
  12. }

  13. /// use `Fn`, `FnMut` or `FnOnce` traits to defined JavaScript callbacks
  14. /// the return type of callbacks can only be `Result`.
  15. #[napi]
  16. fn get_cwd<T: Fn(String) -> Result<()>>(callback: T) {
  17.   callback(env::current_dir().unwrap().to_string_lossy().to_string()).unwrap();
  18. }

  19. /// or, define the callback signature in where clause
  20. #[napi]
  21. fn test_callback<T>(callback: T)
  22. where T: Fn(String) -> Result<()>
  23. {}

  24. /// async fn, require `async` feature enabled.
  25. /// [dependencies]
  26. /// napi = {version="2", features=["async"]}
  27. #[napi]
  28. async fn read_file_async(path: String) -> Result<Buffer> {
  29.   tokio::fs::read(path)
  30.     .map(|r| match r {
  31.       Ok(content) => Ok(content.into()),
  32.       Err(e) => Err(Error::new(
  33.         Status::GenericFailure,
  34.         format!("failed to read file, {}", e),
  35.       )),
  36.     })
  37.     .await
  38. }
  39. ```

more examples at examples

Building


This repository is a Cargocrate. Any napi-based add-on should contain Cargo.tomlto make it a Cargo crate.

In your Cargo.tomlyou need to set the crate-typeto "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.

  1. ``` toml
  2. [package]
  3. name = "awesome"

  4. [lib]
  5. crate-type = ["cdylib"]

  6. [dependencies]
  7. napi = "2"
  8. napi-derive = "2"

  9. [build-dependencies]
  10. napi-build = "1"
  11. ```

And create build.rsin your own project:

  1. ``` rust
  2. // build.rs
  3. extern crate napi_build;

  4. fn main() {
  5.   napi_build::setup();
  6. }
  7. ```

So far, the napibuild script has only been tested on macOSLinuxWindows x64 MSVCand FreeBSD.

Install the @napi-rs/clito help you build your Rustcodes and copy Dynamic libfile to .nodefile in case you can requireit in your program.

  1. ``` js
  2. {
  3.   "package": "awesome-package",
  4.   "devDependencies": {
  5.     "@napi-rs/cli": "^1.0.0"
  6.   },
  7.   "napi": {
  8.     "name": "jarvis" // <----------- Config the name of native addon, or the napi command will use the name of `Cargo.toml` for the binary file name.
  9.   },
  10.   "scripts": {
  11.     "build": "napi build --release",
  12.     "build:debug": "napi build"
  13.   }
  14. }
  15. ```

Then you can require your native binding:

  1. ``` js
  2. require('./jarvis.node')
  3. ```

The module_namewould be your packagename in your Cargo.toml.

xxx => ./xxx.node

xxx-yyy => ./xxx_yyy.node

You can also copy Dynamic libfile to an appointed location:

  1. ``` shell
  2. napi build [--release] ./dll
  3. napi build [--release] ./artifacts
  4. ```

There are documents which contains more details about the @napi-rs/cliusage.

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_modulesubdirectory.

To run tests:

  1. ``` shell
  2. yarn build:test
  3. yarn test
  4. ```

Related projects


neon
node-bindgen

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 |