近年流行っていると噂のWebAssemblyの環境構築を試したので、忘備録として書いておきます。
フォルダ構成は以下のようになります。
.
├── README.md
├── wasm
│   ├── Cargo.lock
│   ├── Cargo.toml
│   ├── wasm.md
│   ├── pkg
│   ├── src
│   └── target
└── web
    ├── README.md
    ├── bootstrap.js
    ├── index.html
    ├── index.ts
    ├── package-lock.json
    ├── package.json
    ├── tsconfig.json
    ├── web.md
    └── webpack.config.js
コードはgithubに置いています。
Rustの環境構築
使用したRustのバージョンは以下。
$ cargo --version
cargo 1.74.0-nightly (96fe1c9e1 2023-08-29)
$ rustc -V
rustc 1.74.0-nightly (35e416303 2023-09-01)
$ rustup -V
rustup 1.26.0 (5af9b9484 2023-04-05)Rustのlibraryの作成
mkdir wasm
cd wasm
cargo init --libこれでライブラリ用のパッケージ環境が作成されます。
まずはwasm-packをinstallします。
cargo install wasm-pack次にWasmのためのライブラリwasm-bindgenと軽量なメモリアロケータwee_allocをinstallします。
今回用いるCargo.tomlは以下のようになります。
[package]
name = "wasm-sample"
version = "0.1.0"
edition = "2021"
[dependencies]
wasm-bindgen = "0.2.63"
wee_alloc = "0.4.5"
[lib]
crate-type = ["cdylib"]Hello, worldの実装
今回は画面にalertを出すrustのコードを書いてみます。lib.rsを以下のように書き換えます。
use wasm_bindgen::prelude::*;
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
#[wasm_bindgen]
pub fn hello() {
    alert("hello, world!");
}
#[wasm_bindgen]
extern "C" {
    pub fn alert(name: &str);
}ここで、 pub fn alertはJavaScriptのalertを呼び出す関数です。詳しい仕組みは rust-wasmを参照してください。
以下のコマンドでwasmのファイルをビルドします。
wasm-pack build --target webこれでpkgフォルダが作成され、以下のようにwasmのためのコードが生成されたと思います。
├── pkg
│   ├── README.md
│   ├── package.json
│   ├── wasm_sample.d.ts
│   ├── wasm_sample.js
│   ├── wasm_sample_bg.wasm
│   └── wasm_sample_bg.wasm.d.tsこれを適切にフロントエンドから呼び出せばHello, world成功です。これで一旦wasm側は終了となります。
フロントエンドの環境構築
今回は素朴にwebpack+typescriptを用いたフロントエンドの環境を構築します。使用したNode.jsのバージョンは以下。
$ node --version
v16.16.0
$ npm --version
8.11.0NodeModules, wasmのinstall
まずpackage.jsonを以下のように作成します。
{
  "name": "wasm-game",
  "version": "1.0.0",
  "description": "",
  "main": "index.ts",
  "scripts": {
    "dev": "webpack-dev-server",
    "build": "webpack build"
  },
  "keywords": [],
  "author": "ganemaruko",
  "dependencies": {
    "copy-webpack-plugin": "^10.0.0",
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1"
  },
  "devDependencies": {
    "ts-loader": "^9.4.4",
    "typescript": "^5.2.2",
    "webpack-dev-server": "^4.6.0"
  }
}そして、npm installを実行
npm installここで、先ほどの手順でビルドしておいたwasmをnode_modulesに加えます。package.jsonを以下のように変更します。
{
  "name": "wasm-game",
  ...略
  "dependencies": {
    "copy-webpack-plugin": "^10.0.0",
    "webpack": "^5.65.0",
    !この一行を追加!
    "wasm-sample": "file:../wasm/pkg",
    "webpack-cli": "^4.9.1"
  },
  "devDependencies": {
    "ts-loader": "^9.4.4",
    "typescript": "^5.2.2",
    "webpack-dev-server": "^4.6.0"
  }
}wasm-sampleのバージョン部分に相対パスを入れることで、npm install実行時に該当箇所からモジュールをinstallしてくれます。
この状態で以下を実行してください。
npm install wasm-sampleこれでwasmがtypescriptから呼び出せるようになったはずです
また、webpackとtypescriptを用いるので、webpack.config.jsとtsconfig.jsonも作成しておきます。詳細は割愛しますが、以下のようになります。
const path = require("path");
const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = {
  entry: "./index.ts",
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "index.js",
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js"],
  },
  mode: "development",
  plugins: [
    new CopyWebpackPlugin({
      patterns: [{ from: "./index.html", to: "./" }],
    }),
  ],
};index.tsをコンパイルしてpublic/index.jsに出力する、というところだけ押さえていただければ大丈夫です。
合わせてtsconfig.jsonは以下になります。
{
    "compilerOptions": {
      "outDir": "./public/",
      "noImplicitAny": true,
      "module": "es6",
      "target": "es5",
      "allowJs": true,
      "moduleResolution": "node"
    }
  }これてtypescriptからwasmを呼ぶ出す準備が整いました。
index.tsの作成
wasmファイルを呼び出すindex.tsを作成します。以下のようなコードになります。
import init, { hello } from "wasm-sample";
init().then((_) => {
  hello();
  console.log("OK!");
});wasm-sampleが正しくinstallできていればhelloにきちんと型が当たっているはずです。

先ほどのwebpack.config.jsでこのindex.tsがpublic/index.jsにコンパイルされるように設定されているので、index.htmlを以下のように作成します。
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Wasm Sample</title>
  </head>
  <body>
    <script src="./index.js"></script>
  </body>
</html>
これで準備OKです。webpack-dev-serverを起動します。
npm run devこれでlocalhost:8080を確認すると、以下のようにalertが上がっているのが確認できると思います。

これでrustをコンパイルしたwasmをtypescriptから呼びだせました。
 
  
  
  
  

コメント