docs: update documents

This commit is contained in:
LongYinan 2020-12-03 17:17:40 +08:00
parent 66088642ee
commit f90640d7aa
No known key found for this signature in database
GPG key ID: A3FFE134A3E20881
7 changed files with 86 additions and 63 deletions

View file

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

View file

@ -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>);

View file

@ -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 {

View file

@ -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,

View file

@ -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`.

View file

@ -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> {

View file

@ -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