6个版本
0.3.0 | 2023年9月27日 |
---|---|
0.2.3 | 2021年7月15日 |
0.2.2 | 2021年3月15日 |
0.2.1 | 2020年10月15日 |
0.1.0 | 2020年2月4日 |
#219 in 构建工具
每月478次下载
用于 2 个crate(通过 node-bindgen)
9KB
194 行
node-bindgen
特性
- 简单:只需编写惯用Rust代码,node-bindgen将负责生成Node.js FFI包装器代码。
- 安全:根据Rust类型自动检查Node.js参数。
- 异步:支持异步Rust。异步代码将转换为Node.js承诺。
- 类:可以使用Node.js类访问Rust结构。
- 流:使用Rust实现Node.js流。
- N-API:使用Node.js N-API,这意味着您不需要重新编译您的模块。
Node.js版本兼容性
此项目使用Node N-API的v8。请参阅以下兼容性矩阵。
以下操作系统支持
- Linux
- MacOs
- Windows
为什么选择node-bindgen?
编写原生node-js需要大量的样板代码。node-bindgen从Rust代码生成外部"C"粘合代码,包括原生模块注册。node-bindgen使编写Node.js模块变得简单且有趣。
入门指南
CLI安装
安装nj-cli命令行,该命令行将用于生成原生库。
cargo install nj-cli
这是一个一次性步骤。
配置Cargo.toml
将两个依赖项添加到项目的Cargo.toml
中。
然后添加node-bindgen
作为常规依赖项(如下所示)
[dependencies]
node-bindgen = { version = "6.0" }
然后添加node-bindgen
的过程宏到您的构建依赖项中,如下所示
[build-dependencies]
node-bindgen = { version = "6.0", default-features = false, features = ["build"] }
然后更新crate类型为cdylib
以生成兼容Node.js的原生模块
[lib]
crate-type = ["cdylib"]
最后,在项目顶部添加build.rs
,内容如下
fn main() {
node_bindgen::build::configure();
}
示例
这是一个添加两个数字的函数。请注意,您不需要担心JS转换。
use node_bindgen::derive::node_bindgen;
/// add two integer
#[node_bindgen]
fn sum(first: i32, second: i32) -> i32 {
first + second
}
构建原生库
要构建Node.js库,使用nj-cli
进行构建
nj-cli build
这将生成位于"./dist"文件夹中的Node.js模块。
要构建发布版本
nj-cli build --release
监视./src
文件夹的更改
在开发本地模块时,您可能需要监视文件更改,并在更改发生时运行命令,例如 cargo check
或 cargo build
。
为此,我们可以使用 nj-cli watch
。
nj-cli watch
会安装(如果不存在)并将参数传递给 cargo watch
。默认情况下,nj-cli watch
将运行 cargo check
对您的 ./src
文件。
要查看 nj-cli watch
的所有可用方法,请运行以下命令
nj-cli watch-- --help
在Node.js中使用
然后在Node.js中,Rust函数可以像正常的Node.js函数一样调用
$ node
Welcome to Node.js v18.18.0.
Type ".help" for more information.
> let addon = require('./dist');
undefined
> addon.sum(2,3)
5
>
特性
函数名或方法可以重命名,而不是默认映射
#[node_bindgen(name="multiply")]
fn mul(first: i32,second: i32) -> i32 {
first * second
}
Rust函数mul重命名为 multiply
可选参数
如果参数被标记为可选,则可以跳过该参数
#[node_bindgen]
fn sum(first: i32, second: Option<i32>) -> i32 {
first + second.unwrap_or(0)
}
然后sum可以调用为 sum(10)
或 sum(10,20)
回调函数
JS回调函数映射为Rust闭包。
#[node_bindgen]
fn hello<F: Fn(String)>(first: f64, second: F) {
let msg = format!("argument is: {}", first);
second(msg);
}
来自node
let addon = require('./dist');
addon.hello(2,function(msg){
assert.equal(msg,"argument is: 2");
console.log(msg); // print out argument is 2
});
异步Rust也支持回调。
异步Rust支持
异步Rust函数映射为Node.js的Promise。
use std::time::Duration;
use flv_future_aio::time::sleep;
use node_bindgen::derive::node_bindgen;
#[node_bindgen]
async fn hello(arg: f64) -> f64 {
println!("sleeping");
sleep(Duration::from_secs(1)).await;
println!("woke and adding 10.0");
arg + 10.0
}
let addon = require('./dist');
addon.hello(5).then((val) => {
console.log("future value is %s",val);
});
结构体序列化
结构体,包括泛型结构体,可以自动生成转换为JS的样板代码。只需将 node_bindgen
宏应用到您的结构体上
#[node_bindgen]
struct MyJson {
some_name: String,
a_number: i64
}
#[node_bindgen]
fn my_json() -> MyJson {
MyJson {
some_name: "John".to_owned(),
a_number: 1337
}
}
let addon = require('./dist');
assert.deepStrictEqual(addon.my_json(), {
someName: "John",
aNumber: 1337
});
注意,字段必须自己实现 node_bindgen::core::TryIntoJs
。任何引用也必须实现 Clone
。字段名将被转换为camelCase。
枚举
枚举也将使用 node_bindgen
的帮助自动生成其JS表示。
#[node_bindgen]
enum ErrorType {
WithMessage(String, usize),
WithFields {
val: usize
},
UnitErrorType
}
#[node_bindgen]
fn with_message() -> ErrorType {
ErrorType::WithMessage("test".to_owned(), 321)
}
#[node_bindgen]
fn with_fields() -> ErrorType {
ErrorType::WithFields {
val: 123
}
}
#[node_bindgen]
fn with_unit() -> ErrorType {
ErrorType::UnitErrorType
}
assert.deepStrictEqual(addon.withMessage(), {
withMessage: ["test", 321n]
});
assert.deepStrictEqual(addon.withFields(), {
withFields: {
val: 123n
}
});
assert.deepStrictEqual(addon.withUnit(), "UnitErrorType")
元组变体将被转换为列表,结构体变体将转换为对象,单元变体将转换为与变体名称匹配的PascalCase字符串。支持泛型和引用,与结构体相同的注意事项。
JavaScript类
支持JavaScript类。
struct MyClass {
val: f64,
}
#[node_bindgen]
impl MyClass {
#[node_bindgen(constructor)]
fn new(val: f64) -> Self {
Self { val }
}
#[node_bindgen]
fn plus_one(&self) -> f64 {
self.val + 1.0
}
#[node_bindgen(getter)]
fn value(&self) -> f64 {
self.val
}
}
let addon = require('./dist');
const assert = require('assert');
let obj = new addon.MyObject(10);
assert.equal(obj.value,10,"verify value works");
assert.equal(obj.plusOne(),11);
示例文件夹中有更多功能。
Windows + Electron支持
当在Windows上使用node-bindgen与Electron一起使用时,nj-build
必须编译一个C++文件,win_delay_load_hook.cc
,因此开发环境需要有一个有效的C/C++编译器。
如果您的机器没有有效的C/C++编译器,请安装 Microsoft VSCode。
将来,此文件将用Rust重写,删除此依赖。
只需确保您使用以下方式编译Rust模块
$ npx electron-build-env nj-cli build --release
否则,在 electron 中导入 rust 模块时,您将遇到令人厌恶的 动态链接库(DLL)初始化例程失败
。
准备 npm 软件包
使用 node-bindgen
生成的 Node 模块可以直接在任何 Node JS 项目中使用,只需将 index.node
复制进去即可。但是,如果直接访问模块,IDE 将不会突出显示可用的函数、类等。通常,这不太方便,并且当公共 API 发生更改时,会增加潜在错误的几率。
要创建一个带有 TypeScript 类型定义和所有必要的 JavaScript 包装器的完整 npm 软件包,可以使用 tslink crate。
tslink
crate 生成描述 npm 模块的文件 *.d.ts
、*.js
和 package.json
。这样的软件包可以以最小的努力集成到最终项目中。
此外,由于 tslink
生成了 TypeScript 类型定义,因此对原生 node 模块(index.node
)的任何更改都将由 TypeScript
编译器突出显示,这大大降低了与更改的 API 或公共数据类型相关的错误风险。
例如,
#[macro_use] extern crate tslink;
use tslink::tslink;
use node_bindgen::derive::node_bindgen;
struct MyScruct {
inc: i32,
}
#[tslink(class)]
#[node_bindgen]
impl MyScruct {
#[tslink(constructor)]
#[node_bindgen(constructor)]
pub fn new(inc: i32) -> Self {
Self { inc }
}
#[tslink(snake_case_naming)]
#[node_bindgen]
fn inc_my_number(&self, a: i32) -> i32 {
a + self.inc
}
}
将表示为(*.d.ts
)为
export declare class MyStruct {
constructor(inc: number);
incMyNumber(a: number): number;
}
请注意,调用 #[tslink]
应始终在调用 #[node_bindgen]
之前。
另外,请注意,node-bindgen
默认将方法名称转换为蛇形命名。您应使用 #[tslink)
考虑这一点(更多信息请参阅 crate 页面)。
tslink
需要在您的项目根目录的 Cargo.toml
([tslink]
部分)中进行配置。配置应包括指向原生 node 模块的合法路径。默认情况下,node-bindgen
在您的 root
的 ./dist
文件夹中创建 index.node
。
文件:./Cargo.toml
(在项目的 root
中)
[project]
...
[lib]
...
[tslink]
node = "./dist/index.node"
使用 tslink
和 node-bindgen
的完整示例 在此。
有关 tslink
的更多 API 文档,请参阅 crate 页面。
注意。node-bindgen 的开发者不对 tslink crate 的工作的正确性负责。所有与 tslink 相关的可能问题和功能请求都应提交给 tslink 的开发者。
贡献
如果您想为项目做出贡献,请阅读我们的 贡献指南。
许可
此项目根据 Apache 许可证 许可。
依赖项
~0–9MB
~85K SLoC