docs: update documents
This commit is contained in:
parent
66088642ee
commit
f90640d7aa
7 changed files with 86 additions and 63 deletions
66
README.md
66
README.md
|
@ -32,11 +32,11 @@ A minimal library for building compiled `NodeJS` add-ons in `Rust`.
|
||||||
| ------ | ------- | ------ | ------ |
|
| ------ | ------- | ------ | ------ |
|
||||||
| ✓ | ✓ | ✓ | ✓ |
|
| ✓ | ✓ | ✓ | ✓ |
|
||||||
|
|
||||||
This library depends on N-API and requires `Node@8.9` or later.
|
This library depends on N-API and requires `Node@10.0.0` or later.
|
||||||
|
|
||||||
We already have some packages written by `napi-rs`: [node-rs](https://github.com/napi-rs/node-rs)
|
We already have some packages written by `napi-rs`: [node-rs](https://github.com/napi-rs/node-rs)
|
||||||
|
|
||||||
One nice feature is that this crate allows you to build add-ons purely with the `Rust` toolchain and without involving `node-gyp`.
|
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
|
## Taste
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ fn fibonacci(ctx: CallContext) -> Result<JsNumber> {
|
||||||
ctx.env.create_int64(fibonacci_native(n))
|
ctx.env.create_int64(fibonacci_native(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
fn fibonacci_native(n: i64) -> i64 {
|
fn fibonacci_native(n: i64) -> i64 {
|
||||||
match n {
|
match n {
|
||||||
1 | 2 => 1,
|
1 | 2 => 1,
|
||||||
|
@ -63,31 +63,46 @@ fn fibonacci_native(n: i64) -> i64 {
|
||||||
### Register module
|
### Register module
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
/// test_module is Module name
|
use napi::{JsObject, Result};
|
||||||
register_module!(test_module, init);
|
|
||||||
|
|
||||||
/// Module is `module` object in NodeJS
|
/// `exports` is `module.exports` object in NodeJS
|
||||||
fn init(module: &mut Module) -> Result<()> {
|
#[module_exports]
|
||||||
module.create_named_method("fibonacci", fibonacci)?;
|
fn init(mut exports: JsObject) -> Result<()> {
|
||||||
|
exports.create_named_method("fibonacci", fibonacci)?;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And you can also create `JavaScript` value while registering module:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use napi::{JsObject, Result, Env};
|
||||||
|
|
||||||
|
#[module_exports]
|
||||||
|
fn init(mut exports: JsObject, env: Env) -> Result<()> {
|
||||||
|
exports.create_named_method("fibonacci", fibonacci)?;
|
||||||
|
exports.set_named_property("DEFAULT_VALUE", env.create_int64(100)?)?;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
This repository is a Cargo crate. Any napi-based add-on should contain `Cargo.toml` to make it a Cargo crate.
|
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.
|
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.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
[package]
|
||||||
|
name = "awesome"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
napi = "0.5"
|
napi = "1.0"
|
||||||
napi-derive = "0.5"
|
napi-derive = "1.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
napi-build = "0.2"
|
napi-build = "1.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
And create `build.rs` in your own project:
|
And create `build.rs` in your own project:
|
||||||
|
@ -101,21 +116,24 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
So far, the `napi` build script has only been tested on `macOS` `Linux` and `Windows x64 MSVC`.
|
So far, the `napi` build script has only been tested on `macOS` `Linux` `Windows x64 MSVC` and `FreeBSD`.
|
||||||
|
|
||||||
See the included [test_module](./test_module) for an example add-on.
|
See the included [test_module](./test_module) for an example add-on.
|
||||||
|
|
||||||
Run `cargo build` to produce the `Dynamic lib` file. And install the `napi-rs` to help you copy `Dynamic lib` file to `.node` file in case you can `require` it in your program.
|
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.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"package": "your pkg",
|
"package": "awesome-package",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@napi-rs/cli": "latest"
|
"@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": {
|
"scripts": {
|
||||||
"build": "napi build",
|
"build": "napi build --release",
|
||||||
"build-release": "napi build --release"
|
"build:debug": "napi build"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -123,22 +141,24 @@ Run `cargo build` to produce the `Dynamic lib` file. And install the `napi-rs` t
|
||||||
Then you can require your native binding:
|
Then you can require your native binding:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
require('./target/debug|release/[module_name].node')
|
require('./jarvis.node')
|
||||||
```
|
```
|
||||||
|
|
||||||
The `module_name` would be your `package` name in your `Cargo.toml`.
|
The `module_name` would be your `package` name in your `Cargo.toml`.
|
||||||
|
|
||||||
`xxx => ./target/debug|release/xxx.node`
|
`xxx => ./xxx.node`
|
||||||
|
|
||||||
`xxx-yyy => ./target/debug|release/xxx_yyy.node`
|
`xxx-yyy => ./xxx_yyy.node`
|
||||||
|
|
||||||
You can also copy `Dynamic lib` file to an appointed location:
|
You can also copy `Dynamic lib` file to an appointed location:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
napi build [--release] .
|
napi build [--release] ./dll
|
||||||
napi build [--release] ./mylib
|
napi build [--release] ./artifacts
|
||||||
```
|
```
|
||||||
|
|
||||||
|
There are [documents](./cli) which contains more details about the `@napi-rs/cli` usage.
|
||||||
|
|
||||||
## Testing
|
## 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.
|
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.
|
||||||
|
|
|
@ -3,5 +3,7 @@ pub(crate) struct CleanupEnvHookData<T: 'static> {
|
||||||
pub(crate) hook: Box<dyn FnOnce(T)>,
|
pub(crate) hook: Box<dyn FnOnce(T)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Created by `Env::add_env_cleanup_hook`
|
||||||
|
/// And used by `Env::remove_env_cleanup_hook`
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct CleanupEnvHook<T: 'static>(pub(crate) *mut CleanupEnvHookData<T>);
|
pub struct CleanupEnvHook<T: 'static>(pub(crate) *mut CleanupEnvHookData<T>);
|
||||||
|
|
|
@ -35,6 +35,12 @@ use tokio::sync::mpsc::error::TrySendError;
|
||||||
pub type Callback = extern "C" fn(sys::napi_env, sys::napi_callback_info) -> sys::napi_value;
|
pub type Callback = extern "C" fn(sys::napi_env, sys::napi_callback_info) -> sys::napi_value;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
/// `Env` is used to represent a context that the underlying N-API implementation can use to persist VM-specific state.
|
||||||
|
/// This structure is passed to native functions when they're invoked, and it must be passed back when making N-API calls.
|
||||||
|
/// Specifically, the same `Env` that was passed in when the initial native function was called must be passed to any subsequent nested N-API calls.
|
||||||
|
/// Caching the `Env` for the purpose of general reuse, and passing the `Env` between instances of the same addon running on different Worker threads is not allowed.
|
||||||
|
/// The `Env` becomes invalid when an instance of a native addon is unloaded.
|
||||||
|
/// Notification of this event is delivered through the callbacks given to `Env::add_env_cleanup_hook` and `Env::set_instance_data`.
|
||||||
pub struct Env(pub(crate) sys::napi_env);
|
pub struct Env(pub(crate) sys::napi_env);
|
||||||
|
|
||||||
impl Env {
|
impl Env {
|
||||||
|
|
|
@ -13,6 +13,9 @@ use crate::{sys, Status};
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
/// Represent `JsError`.
|
||||||
|
/// Return this Error in `js_function`, **napi-rs** will throw it as `JsError` for you.
|
||||||
|
/// If you want throw it as `TypeError` or `RangeError`, you can use `JsTypeError/JsRangeError::from(Error).throw_into(env)`
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
pub status: Status,
|
pub status: Status,
|
||||||
|
|
|
@ -6,30 +6,13 @@
|
||||||
//!
|
//!
|
||||||
//! ## Feature flags
|
//! ## Feature flags
|
||||||
//!
|
//!
|
||||||
//! ### libuv
|
//! ### napi1 ~ napi6
|
||||||
//! With `libuv` feature, you can execute a rust future in `libuv` in NodeJS, and return a `promise` object.
|
|
||||||
//! ```
|
|
||||||
//! use std::thread;
|
|
||||||
//! use std::fs;
|
|
||||||
//!
|
//!
|
||||||
//! use futures::prelude::*;
|
//! Because `NodeJS` N-API has versions. So there are feature flags to choose what version of `N-API` you want to build for.
|
||||||
//! use futures::channel::oneshot;
|
//! For example, if you want build a library which can be used by `node@10.17.0`, you should choose the `napi5` or lower.
|
||||||
//! use napi::{CallContext, Result, JsString, JsObject, Status, Error};
|
//!
|
||||||
|
//! The details of N-API versions and support matrix: [n_api_n_api_version_matrix](https://nodejs.org/api/n-api.html#n_api_n_api_version_matrix)
|
||||||
//!
|
//!
|
||||||
//! #[js_function(1)]
|
|
||||||
//! pub fn uv_read_file(ctx: CallContext) -> Result<JsObject> {
|
|
||||||
//! let path = ctx.get::<JsString>(0)?;
|
|
||||||
//! let (sender, receiver) = oneshot::channel();
|
|
||||||
//! let p = path.as_str()?.to_owned();
|
|
||||||
//! thread::spawn(|| {
|
|
||||||
//! let res = fs::read(p).map_err(|e| Error::new(Status::Unknown, format!("{}", e)));
|
|
||||||
//! sender.send(res).expect("Send data failed");
|
|
||||||
//! });
|
|
||||||
//! ctx.env.execute(receiver.map_err(|e| Error::new(Status::Unknown, format!("{}", e))).map(|x| x.and_then(|x| x)), |&mut env, data| {
|
|
||||||
//! env.create_buffer_with_data(data)
|
|
||||||
//! })
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//! ### tokio_rt
|
//! ### tokio_rt
|
||||||
//! With `tokio_rt` feature, `napi-rs` provides a ***tokio runtime*** in an additional thread.
|
//! With `tokio_rt` feature, `napi-rs` provides a ***tokio runtime*** in an additional thread.
|
||||||
//! And you can easily run tokio `future` in it and return `promise`.
|
//! And you can easily run tokio `future` in it and return `promise`.
|
||||||
|
|
|
@ -41,7 +41,7 @@ impl Into<napi_threadsafe_function_call_mode> for ThreadsafeFunctionCallMode {
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// An example of using `ThreadsafeFunction`:
|
/// An example of using `ThreadsafeFunction`:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```rust
|
||||||
/// #[macro_use]
|
/// #[macro_use]
|
||||||
/// extern crate napi_derive;
|
/// extern crate napi_derive;
|
||||||
///
|
///
|
||||||
|
@ -53,28 +53,36 @@ impl Into<napi_threadsafe_function_call_mode> for ThreadsafeFunctionCallMode {
|
||||||
/// },
|
/// },
|
||||||
/// CallContext, Error, JsFunction, JsNumber, JsUndefined, Result, Status,
|
/// CallContext, Error, JsFunction, JsNumber, JsUndefined, Result, Status,
|
||||||
/// };
|
/// };
|
||||||
|
///
|
||||||
/// #[js_function(1)]
|
/// #[js_function(1)]
|
||||||
/// pub fn test_threadsafe_function(ctx: CallContext) -> Result<JsUndefined> {
|
/// pub fn test_threadsafe_function(ctx: CallContext) -> Result<JsUndefined> {
|
||||||
/// let func = ctx.get::<JsFunction>(0)?;
|
/// let func = ctx.get::<JsFunction>(0)?;
|
||||||
|
///
|
||||||
/// let tsfn = ctx.env
|
/// let tsfn =
|
||||||
/// .create_threadsafe_function(func, 0, |ctx: ThreadSafeCallContext<Vec<u32>>| {
|
/// ctx
|
||||||
|
/// .env
|
||||||
|
/// .create_threadsafe_function(&func, 0, |ctx: ThreadSafeCallContext<Vec<u32>>| {
|
||||||
/// ctx.value
|
/// ctx.value
|
||||||
/// .iter()
|
/// .iter()
|
||||||
/// .map(|v| ctx.env.create_uint32(*v))]
|
/// .map(|v| ctx.env.create_uint32(*v))
|
||||||
/// .collect::<Result<Vec<JsNumber>>>()
|
/// .collect::<Result<Vec<JsNumber>>>()
|
||||||
/// })?;
|
/// })?;
|
||||||
|
///
|
||||||
/// thread::spawn(move || {
|
/// let tsfn_cloned = tsfn.try_clone()?;
|
||||||
/// let output: Vec<u32> = vec![42, 1, 2, 3];
|
///
|
||||||
/// // It's okay to call a threadsafe function multiple times.
|
/// thread::spawn(move || {
|
||||||
/// tsfn.call(Ok(output.clone()), ThreadsafeFunctionCallMode::Blocking);
|
/// let output: Vec<u32> = vec![0, 1, 2, 3];
|
||||||
/// tsfn.call(Ok(output.clone()), ThreadsafeFunctionCallMode::NonBlocking);
|
/// // It's okay to call a threadsafe function multiple times.
|
||||||
/// tsfn.release(ThreadsafeFunctionReleaseMode::Release);
|
/// tsfn.call(Ok(output.clone()), ThreadsafeFunctionCallMode::Blocking);
|
||||||
/// });
|
/// });
|
||||||
|
///
|
||||||
/// ctx.env.get_undefined()
|
/// thread::spawn(move || {
|
||||||
|
/// let output: Vec<u32> = vec![3, 2, 1, 0];
|
||||||
|
/// // It's okay to call a threadsafe function multiple times.
|
||||||
|
/// tsfn_cloned.call(Ok(output.clone()), ThreadsafeFunctionCallMode::NonBlocking);
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// ctx.env.get_undefined()
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct ThreadsafeFunction<T: 'static> {
|
pub struct ThreadsafeFunction<T: 'static> {
|
||||||
|
|
|
@ -43,6 +43,7 @@ pub(crate) fn get_tokio_sender() -> &'static mpsc::Sender<Message> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
pub unsafe extern "C" fn shutdown(_data: *mut c_void) {
|
pub unsafe extern "C" fn shutdown(_data: *mut c_void) {
|
||||||
let sender = get_tokio_sender().clone();
|
let sender = get_tokio_sender().clone();
|
||||||
sender
|
sender
|
||||||
|
|
Loading…
Reference in a new issue