RustでビルドしたWASMをNode.jsで扱う方法
もちべ~しょん
最近typescriptを用いてNode.jsでぼちぼち遊んでるんだけど、そいやWASMでビルドしたRustのライブラリ読み込めないかなというのが元々のとっかかりだった。加えてECMAScript modulesとしてパッケージングする方法が出てこなかった。そんな中、ECMAScript modulesとしてパッケージの作成と利用に成功したこともあってとりあえず現時点での知見をまとめとこうかなぁって。
利用ライブラリとか
- OS Windows11
- Node.js Ver 23.4.0
- tsx 4.19.2
- typescript 5.7.2
- nightly-x86_64-pc-windows-msvc (default)
- rustc 1.85.0-nightly (327c7ee43 2024-12-13)
仕込み
Node.js及びCargoは既に導入済みで利用可能という前提で進めていくので、その点何卒ご了承の程
Rust側
まず、wasm32-unknown-unknown
ターゲットを利用中のToolchainに追加する必要がある。rustupが既に利用可能なら、以下のコマンドで導入が完了する。
rustup target add wasm32-unknown-unknown
次に、wasm-bindegen
のCLIを導入する。これもcargo
が導入済みなら以下のコマンドでインストールできる
cargo install -f wasm-bindgen-cli
以上でRust側の仕込みは完了となる[1]
Node.js側
Node.Js側で必要な事前準備は特にないので省略。ただ、今回はpnpm
を使うので必要であれば導入しておく必要がある。
Rust側の実装とパッケージ作成
それでは早速Rust側の実装とビルドを行っていくことにしよう。
プロジェクトを作成する
以下のコマンドを実行してとりあえずLibクレートをこさえる
cargo new wasm_lib --lib
次に、作成されたCargo.toml
を以下のように変更しよう
[package]
name = "wasm_lib"
version = "0.1.0"
edition = "2024"
[lib]
# Cdylibを指定
crate-type = ["cdylib"]
[dependencies]
#wasm-bindgenを追加
wasm-bindgen = ">=0.2"
実装
ここまでできたら早速実装に移ろう。今回は単純に整数の足し算を定義してその関数を公開することにする。lib.rs
を以下のように実装した。
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add_i64(left: i64, right: i64) -> i64 {
left + right
}
wasm_bindgen
Attributeを指定しておくことを忘れずにしておこう。
ビルドとパッケージ作成
ここまでできたら早速ビルドする。
cargo build --target wasm32-unknown-unknown --release
--release
の有無は任意で構わない。
次に、ビルドしたwasmをNode.jsで利用できるようにパッケージを作成する。今回はwasm-pack
が残念ながら使えないので、wasm-bindgen
のCLIを使うことにした。
wasm-bindgen --target experimental-nodejs-module --out-dir .\pkg .\target\wasm32-unknown-unknown\release\wasm_lib.wasm
このコマンドの実行後、プロジェクトフォルダ直下にpkg
というフォルダが生成される。その中のpackage.json
を以下のように編集する。
{
"name": "wasm_lib",
"version": "1.0.0",
"main": "wasm_lib.js",
"types": "wasm_lib.d.ts",
"type": "module",
"files": [
"wasm_lib.js",
"wasm_lib.d.ts",
"wasm_lib_bg.wasm"
]
}
ちなみに、wasm-bindgen
を実行すると、全て上書きされてしまうので、適宜別の場所に保存しておいた方が良いかもしれない。
ここまででRust方面の操作は終了となる。
Node.jsの準備
ここから先は、Node.jsとTypeScriptの操作になる。まずはプロジェクトをこさえよう
pmpn init
ここで、以下のようにpackage.json
を編集しておく
{
"name": "hello_wasm",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "module"
}
次に、必要なパッケージをインストールしよう
pnpm add typescript tsx -D
そして、先に作成しておいたpkgを追加する(ここではプロジェクトフォルダ直下にコピーしている)
pnpm add ./pkg
また、link
したければ、以下のようになる
pnpm link ./pkg
次に、tsconfig.json
を以下のように定義しておこう
{
"compilerOptions": {
"target": "ES2023",
"module": "ES2022",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"moduleResolution": "Node16"
}
}
(´・ω・`)実行よ~
ここまでできたらあと一息。プロジェクトフォルダ直下にindex.ts
をこさえ手、内容を以下のようにする
import {add_i64} from "wasm_lib";
let ans = add_i64(42n, 114514n);
console.log(ans)
ここまでできたら、とりあえず、tsx
を使って動くのか確認してみよう
PS ...> pnpm exec tsx .\index.ts
114556n
以上のように実行できたら成功かなって。
ちなみにビルドして実行するなら
pnpm exec tsc ;pnpm exec node ./index.js
これでも同様に実行できるはずである。
まとめ
WebやBundlerのビルド方法とその利用方法はそれなりにあったんだけど、Node.jsでJavaScript moduleとして利用する方法が殆ど無かったので自分の備忘録を兼ねてまとめてみた。
Discussion