Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Easy way to write Node.js module using Rust

License

NotificationsYou must be signed in to change notification settings

infinyon/node-bindgen

Repository files navigation

Easy way to write native Node.js module using idiomatic Rust

CI StatusCrates.io versionDownloaddocs.rs docschat

Features

  • Easy: Just write idiomatic Rust code, node-bindgen take care of generating Node.js FFI wrapper codes.
  • Safe: Node.js arguments are checked automatically based on Rust types.
  • Async: Support Async Rust. Async codes are translated into Node.js promises.
  • Class: Rust struct can be accessed using Node.js classes.
  • Stream: Implement Node.js stream using Rust
  • N-API: Use Node.js N-API, which means you don't have to recompile your module.

Compatibility with Node.js version

This project uses the v8 of Node N-API. Please see followingcompatibility matrix.

Following OS are supported:

  • Linux
  • MacOs
  • Windows

Why node-bindgen?

Writing native node-js requires lots of boilerplate code. Node-bindgen generates external "C" glue code from rust code, including native module registration. node-bindgen make it writing node-js module easy and fun.

Getting started

CLI Installation

Install nj-cli command line, which will be used to generate the native library.

cargo install nj-cli

This is a one time step.

Configuring Cargo.toml

Add two dependencies to your projects'Cargo.toml.

Addnode-bindgen as a regular dependency (as below):

[dependencies]node-bindgen = {version ="6.0" }

Then addnode-bindgen's procedure macro to your build-dependencies as below:

[build-dependencies]node-bindgen = {version ="6.0",default-features =false,features = ["build"] }

Then update crate type tocdylib to generate node.js compatible native module:

[lib]crate-type = ["cdylib"]

Finally, addbuild.rs at the top of the project with following content:

fnmain(){    node_bindgen::build::configure();}

Example

Here is a function that adds two numbers. Note that you don't need to worry about JS conversion.

use node_bindgen::derive::node_bindgen;/// add two integer#[node_bindgen]fnsum(first:i32,second:i32) ->i32{    first + second}

Building native library

To build node.js library, usingnj-cli to build:

nj-cli build

This will generate Node.js module in "./dist" folder.

To build a release version:

nj-cli build --release

Watching./src for Changes

While developing your native module, you may want to watch for file changes and run a command when a change occurs, for examplecargo check orcargo build.

For this, we can usenj-cli watch.

nj-cli watch installs [if it does not exist] and passes arguments tocargo watch. By default,nj-cli watch will runcargo check against your./src files.

To see all available methods fornj-cli watch, run the following command:

nj-cli watch -- --help

Using in Node.js

Then in the Node.js, rust function can be invoked as normal node.js function:

$nodeWelcometoNode.jsv18.18.0.Type".help"formoreinformation.>letaddon=require('./dist');undefined>addon.sum(2,3)5>

Features

Function name or method can be renamed instead of default mapping

#[node_bindgen(name="multiply")]fnmul(first:i32,second:i32) ->i32{    first* second}

Rust function mul is re-mapped asmultiply

Optional argument

Argument can be skipped if it is marked as optional

#[node_bindgen]fnsum(first:i32,second:Option<i32>) ->i32{    first + second.unwrap_or(0)}

Then sum can be invoked assum(10) orsum(10,20)

Callback

JS callback are mapped as Rust closure.

#[node_bindgen]fnhello<F:Fn(String)>(first:f64,second:F){let msg =format!("argument is: {}", first);second(msg);}

from node:

letaddon=require('./dist');addon.hello(2,function(msg){assert.equal(msg,"argument is: 2");console.log(msg);// print out argument is 2});

Callback are supported in Async rust as well.

Support for Async Rust

Async rust function is mapped to Node.js promise.

use std::time::Duration;use flv_future_aio::time::sleep;use node_bindgen::derive::node_bindgen;#[node_bindgen]asyncfnhello(arg:f64) ->f64{println!("sleeping");sleep(Duration::from_secs(1)).await;println!("woke and adding 10.0");    arg +10.0}
letaddon=require('./dist');addon.hello(5).then((val)=>{console.log("future value is %s",val);});

Struct serialization

Structs, including generic structs, can have have the to-JS conversion boilerplate autogenerated.Just apply thenode_bindgen macro to your struct:

#[node_bindgen]structMyJson{some_name:String,a_number:i64}#[node_bindgen]fnmy_json() ->MyJson{MyJson{some_name:"John".to_owned(),a_number:1337}}
letaddon=require('./dist');assert.deepStrictEqual(addon.my_json(),{someName:"John",aNumber:1337});

Note that the fields must implementnode_bindgen::core::TryIntoJs themselves.Any references must also implementClone.Field names will be converted to camelCase.

Enums

Enums will also have their JS representation autogenerated with the help ofnode_bindgen:

#[node_bindgen]enumErrorType{WithMessage(String,usize),WithFields{val:usize},UnitErrorType}#[node_bindgen]fnwith_message() ->ErrorType{ErrorType::WithMessage("test".to_owned(),321)}#[node_bindgen]fnwith_fields() ->ErrorType{ErrorType::WithFields{val:123}}#[node_bindgen]fnwith_unit() ->ErrorType{ErrorType::UnitErrorType}
assert.deepStrictEqual(addon.withMessage(),{withMessage:["test",321n]});assert.deepStrictEqual(addon.withFields(),{withFields:{val:123n}});assert.deepStrictEqual(addon.withUnit(),"UnitErrorType")

Tuple variants will be converted into lists, struct variants converted to objects, and unit variants converted into strings matching the variant's name in PascalCase.Generics and references are supported, with the same caveats as for structs.

JavaScript class

JavaScript class is supported.

structMyClass{val:f64,}#[node_bindgen]implMyClass{#[node_bindgen(constructor)]fnnew(val:f64) ->Self{Self{ val}}#[node_bindgen]fnplus_one(&self) ->f64{self.val +1.0}#[node_bindgen(getter)]fnvalue(&self) ->f64{self.val}}
letaddon=require('./dist');constassert=require('assert');letobj=newaddon.MyObject(10);assert.equal(obj.value,10,"verify value works");assert.equal(obj.plusOne(),11);

There are more features in the examples folder.

Windows + Electron Support

When using node-bindgen with electron on Windows,nj-build mustcompile a C++ file,win_delay_load_hook.cc, and therefore it is required that the developmentenvironment has a valid C/C++ compiler.

If your machine does not have a valid C/C++ compiler, installMicrosoft VSCode.

In the future, this file will be re-written in Rust, removing this dependency.

Just make sure that you are compiling the rust module using

npx electron-build-env nj-cli build --release

otherwise you will get dreadedA dynamic link library (DLL) initialization routine failed when importing the rust module in electron

Preparing npm packages

Node module generated withnode-bindgen can be used directly in any node JS project, just copiedindex.node into it. But in case of direct access to a module IDE will not highlight available functions, classes etc. Usually, this is not comfortable and makes the risks of potential bugs higher as soon as the public API of the node module is changed.

To create a full-fledged npm package with TypeScript types definitions and all necessary JavaScript wrappers can be used a cratetslink.

tslink crate generates files*.d.ts,*.js andpackage.json with a description of the npm module. Such package could be integrated into an end-project with minimal effort.

In addition, becausetslink generates TypeScript types definitions, any changes on the native node module (index.node) will be highlighted byTypeScript compiler and it makes the risk of bugs (related to changed API or public data types) much lower.

For example,

#[macro_use]externcrate tslink;use tslink::tslink;use node_bindgen::derive::node_bindgen;structMyScruct{inc:i32,}#[tslink(class)]#[node_bindgen]implMyScruct{#[tslink(constructor)]#[node_bindgen(constructor)]pubfnnew(inc:i32) ->Self{Self{ inc}}#[tslink(snake_case_naming)]#[node_bindgen]fninc_my_number(&self,a:i32) ->i32{        a +self.inc}}

Would be represented (*.d.ts) as

exportdeclareclassMyStruct{constructor(inc:number);incMyNumber(a:number):number;}

Pay your attention, call of#[tslink] should be always above of call#[node_bindgen].

Also, pleasenote,node-bindgen by default applies snake case naming to methods. You should use#[tslink(snake_case_naming)] to consider this moment (see more oncrate page).

tslink requires a configuration inCargo.toml (section[tslink]) of the root of your project. A configuration should include a valid path to the native node module. By defaultnode-bindgen createsindex.node in./dist folder of yourroot.

File:./Cargo.toml (in aroot of project):

[project]# ...[lib]# ...[tslink]node ="./dist/index.node"

Full example of usagetslink andnode-bindgen ishere.

See more API documentation on atslinkcrate page.

Note. The node-bindgen's developers are not responsible for the correctness of the work tslink crate. All possible issues and feature requests related to tslink should be addressed to tslink's developers.

Contributing

If you'd like to contribute to the project, please read ourContributing guide.

License

This project is licensed under theApache license.

About

Easy way to write Node.js module using Rust

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published

Contributors27

Languages


[8]ページ先頭

©2009-2025 Movatter.jp