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)
|
||||
|
||||
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
|
||||
|
||||
|
@ -51,7 +51,7 @@ fn fibonacci(ctx: CallContext) -> Result<JsNumber> {
|
|||
ctx.env.create_int64(fibonacci_native(n))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
fn fibonacci_native(n: i64) -> i64 {
|
||||
match n {
|
||||
1 | 2 => 1,
|
||||
|
@ -63,31 +63,46 @@ fn fibonacci_native(n: i64) -> i64 {
|
|||
### Register module
|
||||
|
||||
```rust
|
||||
/// test_module is Module name
|
||||
register_module!(test_module, init);
|
||||
use napi::{JsObject, Result};
|
||||
|
||||
/// Module is `module` object in NodeJS
|
||||
fn init(module: &mut Module) -> Result<()> {
|
||||
module.create_named_method("fibonacci", fibonacci)?;
|
||||
/// `exports` is `module.exports` object in NodeJS
|
||||
#[module_exports]
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "awesome"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
napi = "0.5"
|
||||
napi-derive = "0.5"
|
||||
napi = "1.0"
|
||||
napi-derive = "1.0"
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = "0.2"
|
||||
napi-build = "1.0"
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
{
|
||||
"package": "your pkg",
|
||||
"package": "awesome-package",
|
||||
"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": {
|
||||
"build": "napi build",
|
||||
"build-release": "napi build --release"
|
||||
"build": "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:
|
||||
|
||||
```js
|
||||
require('./target/debug|release/[module_name].node')
|
||||
require('./jarvis.node')
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```bash
|
||||
napi build [--release] .
|
||||
napi build [--release] ./mylib
|
||||
napi build [--release] ./dll
|
||||
napi build [--release] ./artifacts
|
||||
```
|
||||
|
||||
There are [documents](./cli) 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.
|
||||
|
|
|
@ -3,5 +3,7 @@ pub(crate) struct CleanupEnvHookData<T: 'static> {
|
|||
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)]
|
||||
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;
|
||||
|
||||
#[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);
|
||||
|
||||
impl Env {
|
||||
|
|
|
@ -13,6 +13,9 @@ use crate::{sys, Status};
|
|||
|
||||
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)]
|
||||
pub struct Error {
|
||||
pub status: Status,
|
||||
|
|
|
@ -6,30 +6,13 @@
|
|||
//!
|
||||
//! ## Feature flags
|
||||
//!
|
||||
//! ### libuv
|
||||
//! With `libuv` feature, you can execute a rust future in `libuv` in NodeJS, and return a `promise` object.
|
||||
//! ```
|
||||
//! use std::thread;
|
||||
//! use std::fs;
|
||||
//! ### napi1 ~ napi6
|
||||
//!
|
||||
//! use futures::prelude::*;
|
||||
//! use futures::channel::oneshot;
|
||||
//! use napi::{CallContext, Result, JsString, JsObject, Status, Error};
|
||||
//! Because `NodeJS` N-API has versions. So there are feature flags to choose what version of `N-API` you want to build for.
|
||||
//! For example, if you want build a library which can be used by `node@10.17.0`, you should choose the `napi5` or lower.
|
||||
//!
|
||||
//! 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
|
||||
//! 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`.
|
||||
|
|
|
@ -41,7 +41,7 @@ impl Into<napi_threadsafe_function_call_mode> for ThreadsafeFunctionCallMode {
|
|||
/// ## Example
|
||||
/// An example of using `ThreadsafeFunction`:
|
||||
///
|
||||
/// ```
|
||||
/// ```rust
|
||||
/// #[macro_use]
|
||||
/// extern crate napi_derive;
|
||||
///
|
||||
|
@ -53,28 +53,36 @@ impl Into<napi_threadsafe_function_call_mode> for ThreadsafeFunctionCallMode {
|
|||
/// },
|
||||
/// CallContext, Error, JsFunction, JsNumber, JsUndefined, Result, Status,
|
||||
/// };
|
||||
|
||||
///
|
||||
/// #[js_function(1)]
|
||||
/// pub fn test_threadsafe_function(ctx: CallContext) -> Result<JsUndefined> {
|
||||
/// let func = ctx.get::<JsFunction>(0)?;
|
||||
|
||||
/// let tsfn = ctx.env
|
||||
/// .create_threadsafe_function(func, 0, |ctx: ThreadSafeCallContext<Vec<u32>>| {
|
||||
/// let func = ctx.get::<JsFunction>(0)?;
|
||||
///
|
||||
/// let tsfn =
|
||||
/// ctx
|
||||
/// .env
|
||||
/// .create_threadsafe_function(&func, 0, |ctx: ThreadSafeCallContext<Vec<u32>>| {
|
||||
/// ctx.value
|
||||
/// .iter()
|
||||
/// .map(|v| ctx.env.create_uint32(*v))]
|
||||
/// .map(|v| ctx.env.create_uint32(*v))
|
||||
/// .collect::<Result<Vec<JsNumber>>>()
|
||||
/// })?;
|
||||
|
||||
/// thread::spawn(move || {
|
||||
/// let output: Vec<u32> = vec![42, 1, 2, 3];
|
||||
/// // It's okay to call a threadsafe function multiple times.
|
||||
/// tsfn.call(Ok(output.clone()), ThreadsafeFunctionCallMode::Blocking);
|
||||
/// tsfn.call(Ok(output.clone()), ThreadsafeFunctionCallMode::NonBlocking);
|
||||
/// tsfn.release(ThreadsafeFunctionReleaseMode::Release);
|
||||
/// });
|
||||
|
||||
/// ctx.env.get_undefined()
|
||||
/// })?;
|
||||
///
|
||||
/// let tsfn_cloned = tsfn.try_clone()?;
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let output: Vec<u32> = vec![0, 1, 2, 3];
|
||||
/// // It's okay to call a threadsafe function multiple times.
|
||||
/// tsfn.call(Ok(output.clone()), ThreadsafeFunctionCallMode::Blocking);
|
||||
/// });
|
||||
///
|
||||
/// 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> {
|
||||
|
|
|
@ -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) {
|
||||
let sender = get_tokio_sender().clone();
|
||||
sender
|
||||
|
|
Loading…
Reference in a new issue