依存ツール
依存は以下のコマンドでインストールします。
# wasm-pack のインストールがまだの場合
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
# npm
yarn add -D \
@wasm-tool/wasm-pack-plugin \
html-webpack-plugin \
ts-loader \
typescript \
webpack \
webpack-cli \
webpack-dev-server \
1. wasm-pack
wasm-packは、 Rust コードを WebAssembly へコンパイルし、JavaScript で扱うためのパッケージ化を行ってくれます。
吐き出される JavaScript は完全ではないですが TypeScript ですぐ使える形になっているので、ここでも TypeScript で見ていこうと思います。
2. Webpack
3. webpack-dev-server
wasm-packWebpackwebpack-dev-server は、 Webpack によるビルド後の結果でサーバーを建ててくれます。ビルドは行いますが、インメモリなファイルとして扱われるので、実際にあるディレクトリ内のファイルにビルド結果を出力したりはしません。
また、関連ファイルに変更が入った際に自動リロードで助けてくれます。
4. html-webpack-plugin
wasm-packWebpackwebpack-dev-serverhtml-webpack-plugin は、 Webpack によるビルドで生成されるファイルを読み込んだ形の HTML ファイルを作成してくれます。
設定オプションのfilename
がindex.html
(デフォルト)の時、これはwebpack-dev-serverによるサーバーをブラウザで開いた時に表示される最初の HTML になります。
5. wasm-pack-plugin
wasm-packWebpackwebpack-dev-serverhtml-webpack-pluginwasm-pack-plugin は、 Webpack から wasm-pack を実行する為のプラグインです。これにより webpack
でビルドする際に、 wasm-pack と Webpack 両方のビルドを走らせることができます。
Webpack 設定
以下は設定例です。
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const WasmPackPlugin = require('@wasm-tool/wasm-pack-plugin')
module.exports = (_, argv) => {
return {
mode: argv.mode || 'development',
devtool:
argv.mode === 'production' ? 'source-map' : 'eval-cheap-source-map',
target: 'web',
entry: {
main: path.join(__dirname, 'src/main.ts')
},
output: {
path: path.join(__dirname, 'out'),
filename: '[name].js'
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx', '.wasm']
},
devServer: {
port: 33857
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
options: {
transpileOnly: true
}
}
]
},
plugins: [
new HtmlWebpackPlugin(),
new WasmPackPlugin({
crateDirectory: path.join(__dirname, '.')
})
]
}
}
注意したい点は 2 つです。
resolve.extensions
へ.wasm
を含めるWesmPackPlugin
のcreateDirectory
には、 Cargo プロジェクトのディレクトリを指定する(今回は.
なので Webpack 設定ファイルが置かれているディレクトリに Cargo.toml も置かれている)
この設定でビルドを行うとWesmPackPlugin
は、 Webpack 設定ファイルと同じディレクトリにpkg/
というディレクトリを作ります。これは WebAssemly な JavaScript パッケージなっている為、後はこれをメインとなる JavaScript プロジェクト側から読み込むと、 Rust 側でエクスポートした関数などを実行できます。
Rust
Cargo.toml
は以下のようにします。
[package]
name = "get-started-wasm"
version = "0.0.0"
authors = ["nju33 "]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2.60"
[profile.release]
opt-level = "s"
wasm-bindgen
というパッケージで Rust と JavaScript を紐付けます。
例として、 JavaScript から関数を実行するとコンソールにHello wasm
と出力されるように実装してみます。
まずは、ブラウザのconsole.log
を Rust で実行できるようにする為にsrc/console.rs
ファイルを作ります。
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
pub fn log(s: &str);
}
このextern "C"
ブロックの中に JavaScript 側の関数に関する定義を書くことで、その関数を Rust から使えるようになります。これには#[wasm_bindgen]
属性を付けてあげる必要があります。
この時console.log
のような.
を挟むものはjs_namespace
属性値いを指定してあげる必要があります。(foo.bar.baz
のような深いネストの関数などは、自分でその関数をwindow
以下に設定し、それを定義付けすることで使えました)これでlog("foo")
などでコンソールにログが出せるようになりました。
次に JavaScript から呼ぶ用の関数を作りますが、そのような関数にも#[wasm_bindgen]
属性は必要です。またその関数はResult
を返す必要があります。
今度はsrc/lib.rs
ファイルを作ります。
mod console;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn hello_wasm() -> Result<(), JsValue> {
console::log("Hello Wasm");
Ok(())
}
mod console
で先程のファイルの中身が使えるようになります。例えばlog
はconsole::log
という形と、ほぼ JavaScript と同じ様に使えます。
そしてhelloo_wasm
という関数を定義しました。これによりwasm-pack
で生成された JavaScript パッケージのモジュールからhello_wasm
という関数をインポートできるようになります。
TypeScript
tsconfig.json
を以下のようにします。module: esnext
が必須です。
{
"include": ["src"],
"compilerOptions": {
"target": "es6",
"module": "esnext",
"strict": true,
"esModuleInterop": true,
"moduleResolution": "node",
"isolatedModules": true
}
}
そしてsrc/main.ts
ファイルを作り、以下のようにします。
// eslint-disable-next-line @typescript-eslint/no-floating-promises
;(async () => {
const module = await import('../pkg')
module.hello_wasm()
})()
export {}
重要な点はwasm
なパッケージは必ず Webpack のimport
を使って読み込む事です。.wasm
は今の所この方法による読み込みをしなければエラーになるようです。
後はyarn webpack-dev-server
などからlocalhost
を開き、コンソールにHello wasm
と表示されれば WebAssembly を使うまでの環境構築は完了です。
サンプル
ここまでの動作確認ができるものは wasm-packWebpackwebpack-dev-serverhtml-webpack-pluginwasm-pack-pluginnju33-com/get-started-wasm に置いてあります。