Open62

Rust+WebAssemblyのプロジェクト作成で困ったことについて

so-heyso-hey

Rust and WebAssemblyを読んで,チュートリアルに沿ってwasm-game-of-lifeを作成しようとしたところいくつか問題が発生したため,ここにその修正方法を残す.

so-heyso-hey

cargo generate --git https://github.com/rustwasm/wasm-pack-templateも大丈夫
今回はこの記事のために「wasm-zenn-test」と命名

so-heyso-hey

npm init wasm-app wwwも大丈夫
かつての自分はどこで詰まったんだ?

so-heyso-hey

\wasm-zenn-test\wwwに移動してnpm installを実行すると,

powershell
...
...

42 vulnerabilities (2 low, 5 moderate, 30 high, 5 critical)

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

という結果が得られた.

so-heyso-hey

上の実行結果は

42件の脆弱性(低度2件, 中程度5件, 高度30件, 致命的5件)がある
これらの問題を解決するには npm audit fixを実行する
npm auditを実行することで詳細を見ることができる

と言っている

so-heyso-hey

言われた通り,まずはnpm auditで詳細を見てみよう

so-heyso-hey
poewrshell
...
ansi-html  <0.0.8
Severity: high
Uncontrolled Resource Consumption in ansi-html - https://github.com/advisories/GHSA-whgm-jr23-g3j9
fix available via `npm audit fix`
node_modules/ansi-html
  webpack-dev-server  <=4.7.2
  Depends on vulnerable versions of ansi-html
  Depends on vulnerable versions of chokidar
  Depends on vulnerable versions of selfsigned
  Depends on vulnerable versions of webpack-dev-middleware
  node_modules/webpack-dev-server
...
so-heyso-hey
poowershell
package <version

該当パッケージが特定のバージョン未満であることを示している

powershell
Severity: Level

脆弱性の深刻度を示している

その下はその他の詳しい説明

so-heyso-hey

次にnpm audit fixで破壊的ではないところは修復してみる

so-heyso-hey
powershell
...
...

9 vulnerabilities (1 low, 8 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force
so-heyso-hey

上の実行結果は

9件の脆弱性(低度1件, 高度8件)
破壊的な変更を伴わない問題を解決するにはnpm audit fixを実行する
破壊的な変更を含むすべての問題を解決するにはnpm audit fix --force実行する

と言っている

so-heyso-hey

特にバックアップが必要なものは無いのでnpm audit fix --forceで無理やり修復させてみる

so-heyso-hey

npm audit fix --forceを実行しても

powershell
...
...
5 high severity vulnerabilities

To address all issues, run:
  npm audit fix

などとほざいている

so-heyso-hey

じゃあお望み通りもう一度npm audit fixやってやろうじゃないか

so-heyso-hey
poewrshell
npm error While resolving: webpack-dev-server@5.0.4
npm error Found: webpack@4.43.0
npm error node_modules/webpack
npm error   dev webpack@"^4.29.3" from the root project
npm error   peer webpack@"^4.0.0 || ^5.0.0" from copy-webpack-plugin@5.1.2
npm error   node_modules/copy-webpack-plugin
npm error     dev copy-webpack-plugin@"^5.0.0" from the root project
npm error   2 more (terser-webpack-plugin, webpack-cli)

初めて見るエラーが出た

so-heyso-hey

読んでみると

webpack-dev-serverのv5.0.4を期待したが,v4.43.0が見つかった

的なことを言っている.
バージョンを上げればいい感じ?

so-heyso-hey

まずパッケージのバージョンを確認する.
package.jsonファイルを開いてwebpackとwebpack-dev-serverのバージョンが一致しているか確認

so-heyso-hey

自分の環境では

package.json
...
"devDependencies": {
    "webpack": "^4.29.3",
    "webpack-dev-server": "5.0.4"
    ...
},
...

となっていた.webpackを5.0.4に合わせれば良いっぽい

so-heyso-hey

その為にまず,

powershell
npm install -g npm-check-updates
// or
yarn add global npm-check-updates

npm-check-updatesをインストール.(自分は前者)

so-heyso-hey

ncuコマンドでバージョンアップが必要なパッケージをリストアップ

powershell
ncu

> copy-webpack-plugin   ^5.0.0  →  ^12.0.2
> webpack              ^4.29.3  →  ^5.91.0
> webpack-cli           ^3.1.0  →   ^5.1.4

> Run ncu -u to upgrade package.json
so-heyso-hey

ncu -uコマンドでpackage.jsonを最新バージョンに更新する

poewrshell
ncu -u

> copy-webpack-plugin   ^5.0.0  →  ^12.0.2
> webpack              ^4.29.3  →  ^5.91.0
> webpack-cli           ^3.1.0  →   ^5.1.4

> Run npm install to install new versions.
so-heyso-hey

次に,npmのhello-wasm-packではなくlocalのwasm-zenn-testパッケージを使用したいので,\wasm-zenn-test\www\package.json

package.json
{
    ...
    "dependencies": {
        "wasm-zenn-test": "file:../pkg"
    }
    ...
}

を追加

so-heyso-hey

再びnpm installを実行.これも問題なくいけた

so-heyso-hey

ここでnpm run startでサーバーを開始させようとすると,

powershell
[webpack-cli] Failed to load 'C:\Users\MyName\Desktop\wasm-zenn-test\www\webpack.config.js' config
[webpack-cli] Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
 - options[0] should be an object:
   object { patterns, options? }

というエラーが

so-heyso-hey

これはwebpack.config.jsCopy Pluginの設定がAPIスキーマと一致しないために発生しているらしい.該当箇所を確認してみると,

webpack.config.js
...
module.exports = {
    ...
    plugins: [
        new CopyWebpackPlugin(['index.html'])
    ],
    ...
}

となっていた.

so-heyso-hey

本来CopyWebpackPlugin

webpack.config.js
new CopyWebpackPlugin({
    patterns: [...],
    options: {...},
})

となっているべきらしい(patternsとoptionsで括弧の種類が違うことに注意)

so-heyso-hey

あるべき姿に合わせて修正した

webpack.config.js
new CopyWebpackPlugin({
    patterns: ["index.html"],
    options: {},
})

optionsはとりあえず空にしておいた.後で何かに変更するかも.

追記:opotionsはなくても良いっぽい.

webpack.config.js
new CopyWebpackPlugin({
    patterns: ["index.html"],
})
so-heyso-hey

これでnpm run startを実行すると

powershell
...
modules by path ../pkg/ 1.32 KiB
  ../pkg/wasm_zenn_test_bg.wasm 336 bytes [built] [code generated] [1 error]
...
ERROR in ../pkg/wasm_zenn_test_bg.wasm 1:0
Module parse failed: Unexpected character '' (1:0)
The module seem to be a WebAssembly module, but module is not flagged as WebAssembly module for webpack.
BREAKING CHANGE: Since webpack 5 WebAssembly is not enabled by default and flagged as experimental feature.
You need to enable one of the WebAssembly experiments via 'experiments.asyncWebAssembly: true' (based on async modules) or 'experiments.syncWebAssembly: true' (like webpack 4, deprecated).
For files that transpile to WebAssembly, make sure to set the module type in the 'module.rules' section of the config (e. g. 'type: "webassembly/async"').
(Source code omitted for this binary file)
 @ ../pkg/wasm_zenn_test.js 1:0-49 3:15-19
 @ ./index.js 1:0-39 3:0-10
 @ ./bootstrap.js 4:0-20

webpack 5.91.0 compiled with 1 error in 667 ms
...
so-heyso-hey

これはWebpackがWebAssemblyモジュールとしてフラグが立っていないモジュールを解析しようとして失敗しているらしい.Webpack5からはWebAssemblyはデフォルトでは無効になっており,実験的な機能としてフラグが立てられている.
これを解決するために,Webpackの設定でWebAssemblyの実験を有効にする必要がある.以下のようにwebpack.config.jsファイルを変更する.

webpack.config.js
...
module.exports = {
    ...
    experiments: {
        asyncWebAssembly: true,
    },
}
so-heyso-hey

これでnpm run startを実行すると,うまくいった.完了

so-heyso-hey

まず

powershell
mkdir test-dir
cd test-dir
npm init rust-webpack

すると

powershell
> npx
> create-rust-webpack

 🦀 Rust + 🕸 WebAssembly + Webpack = ❤️
 Installed dependencies ✅

という可愛い実行結果が返ってくる.ここまでは良い,非常に.

so-heyso-hey

ここで,npm run startを実行.
既にwasm-packはインストールされているので,本に書いてあるようなエラーは出なかった.
このエラー解消については,本を参照していただきたい

so-heyso-hey

Rustの最新バージョンであるedition2021を利用するために,Cargo.tomlファイルの設定を

Cargo.toml
...
[package]
...
edition = "2021"
...

とした

so-heyso-hey

これでnpm run startしたら色々エラーが出た

powershell
...
i 「wdm」: Compiled successfully.
i 「wdm」: Compiling...
i 「wdm」: wait until bundle finished: /index.js
× 「wdm」: Hash: fef9f69763b18842e1fd
Version: webpack 4.47.0
Time: 22ms
Built at: 2024/05/03 13:23:37
 2 assets
Entrypoint index = index.js
[./pkg/index.js] 0 bytes {0} [built]
    + 59 hidden modules

ERROR in Rust compilation.
...

これが3セットくらい

so-heyso-hey

まずはcargo buildで依存関係が正しくビルドされることを確認する
→これは問題なし
cargo checkでコードのエラーなどをチェック
→これも問題なし
wasm-packwebpackが正しくインストールされているか確認
webpackがインストールされていなかった

so-heyso-hey

npm install -g webpackwebpackをインストールする

powershell
CLI for webpack must be installed.
  webpack-cli (https://github.com/webpack/webpack-cli)

We will use "npm" to install the CLI via "npm install -D webpack-cli".
Do you want to install 'webpack-cli' (yes/no):

webpack用のCLIがインストールされている必要があるので,今(npm install -D webpack-cliを使って)インストールしますか?みたいなことを言われている.
とりあえずyesでインストールする

so-heyso-hey
powershell
Installing 'webpack-cli' (running 'npm install -D webpack-cli')...

up to date, audited 591 packages in 3s

40 packages are looking for funding
  run `npm fund` for details

10 vulnerabilities (1 low, 9 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.
Error: Cannot find module 'webpack-cli/package.json'
Require stack:
- C:\Users\sohtn\AppData\Roaming\nvm\v21.7.3\node_modules\webpack\bin\webpack.js
    at Module._resolveFilename (node:internal/modules/cjs/loader:1142:15)
    at Function.resolve (node:internal/modules/helpers:190:19)
    at runCli (C:\Users\sohtn\AppData\Roaming\nvm\v21.7.3\node_modules\webpack\bin\webpack.js:78:26)
    at C:\Users\sohtn\AppData\Roaming\nvm\v21.7.3\node_modules\webpack\bin\webpack.js:180:5
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    'C:\\Users\\sohtn\\AppData\\Roaming\\nvm\\v21.7.3\\node_modules\\webpack\\bin\\webpack.js'
  ]
}

という実行結果がでた,,,見たことあるな(勘のいいガキ)

so-heyso-hey

新たにパッケージをインストールしたことでまた脆弱性が見つかったようだ.
npm auditで詳細を確認する.

powershell
10 vulnerabilities (1 low, 9 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force
so-heyso-hey

上の方でやったようにnpm audit fix --forceの後にnpm audit fixで修復を試みる.
同じようなエラーが出たので,先ほどと同じ方法で解消する.

so-heyso-hey
powershell
...
npm error While resolving: copy-webpack-plugin@12.0.2
npm error Found: webpack@4.47.0
npm error node_modules/webpack
npm error   dev webpack@"^4.42.0" from the root project
npm error   peer webpack@"^4.0.0" from terser-webpack-plugin@1.4.5
npm error   node_modules/terser-webpack-plugin
npm error     terser-webpack-plugin@"^1.4.3" from webpack@4.47.0
npm error   1 more (webpack-cli)
...

こんなエラーが出たので,この解消法を詳しくやっていく.

so-heyso-hey

copy-webpack-pluginのv12.0.2はwebpackのv4.47.0と互換性がないので,webpackをv5.xに変更する必要がある.
package.json

package.json
{
    ...
    "devDependencies": {
        ...
        "webpack": "^5.0.0",
        ...
    }
}

としてからnpm audit fixをするとうまくいった

so-heyso-hey

npm run startを実行すると

> rust-webpack-template@0.1.0 start
> rimraf dist pkg && webpack-dev-server --open -d

The command moved into a separate package: @webpack-cli/serve
Would you like to install serve? (That will run npm install -D @webpack-cli/serve) (yes/NO) :

が出て,yesを選択すると

powershell
npm error While resolving: rust-webpack-template@0.1.0
npm error Found: webpack@5.91.0
npm error node_modules/webpack
npm error   dev webpack@"^5.0.0" from the root project
npm error   peer webpack@"5.x.x" from @webpack-cli/serve@2.0.5
npm error   node_modules/@webpack-cli/serve
npm error     dev @webpack-cli/serve@"*" from the root project
npm error
npm error Could not resolve dependency:
npm error peer webpack@"4.x.x" from webpack-cli@3.3.12
npm error node_modules/webpack-cli
npm error   dev webpack-cli@"^3.3.12" from the root project

が出たので,例によって

package.json
{
    ...
    "devDependencies": {
        ...
        "webpack-cli": "^4.0.0",
        ...
    }
}

で再びnpm run start

so-heyso-hey
powershell
npm error While resolving: rust-webpack-template@0.1.0
npm error Found: webpack-cli@4.10.0
npm error node_modules/webpack-cli
npm error   dev webpack-cli@"^4.0.0" from the root project
npm error
npm error Could not resolve dependency:
npm error peer webpack-cli@"5.x.x" from @webpack-cli/serve@2.0.5
npm error node_modules/@webpack-cli/serve
npm error   dev @webpack-cli/serve@"*" from the root project

またこれ(呆)

package.json
{
    ...
    "devDependencies": {
        ...
        "webpack-cli": "^5.0.0",
        ...
    }
}

これでさすがに行けるやろ(フラグ)

so-heyso-hey

npm run startすると

powershell
> rust-webpack-template@0.1.0 start
> rimraf dist pkg && webpack-dev-server --open -d

[webpack-cli] Error: Option '-d, --devtool <value>' argument missing
[webpack-cli] Run 'webpack --help' to see available commands and options

てなった(フラグ回収)

so-heyso-hey

これはpackage.jsonnpm run startのscriptがrimraf dist pkg && webpack-dev-server --open -dに設定されているが,この-dというのはソースマップの種類を指定するためのものなのにソースマップが与えられていないことが原因らしい

so-heyso-hey

これを解消するにはソースマップを引数に与えるか,そもそも-dを消す必要がある.
面倒なので後者をしてから再びnpm run startすると

powershell
> rust-webpack-template@0.1.0 start
> rimraf dist pkg && webpack-dev-server --open

[webpack-cli] Failed to load 'C:\Users\sohtn\Desktop\test-dir\webpack.config.js' config
[webpack-cli] Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
 - options[0] should be an object:
   object { patterns, options? }
so-heyso-hey

上の方で似たエラーが出たので,同様に解消する

webpack.config.js
...
module.exports = {
    ...
    plugins: [
        new CopyPlugin([
            path.resolve(__dirname, "static")
        ]),
        ...
    ],
};

となっていたものを

webpack.config.js
module.exports = {
    ...
    plugins: [
        new CopyPlugin({ patterns: [path.resolve(__dirname, "static")] }),
        ...
    ],
};

と変更した

so-heyso-hey

またまたまたまたまたnpm run startします

powershell
[webpack-cli] Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
 - options has an unknown property 'contentBase'. These properties are valid:
   object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, ipc?, liveReload?, onListening?, open?, port?, proxy?, server?, setupExitSignals?, setupMiddlewares?, static?, watchFiles?, webSocketServer? }

はい(怒)ちなみにここでは分かりづらいかもしれませんが,このメッセージは全て真っ赤なのでかなり怖いしイライラします.

so-heyso-hey

このエラーはwebpack.config.js

webpack.config.js
...
module.exports = {
    ...
    devServer: {
        contentBase: dist,
    },
    ...
};

contentBaseが認識されていないことを示している.
webpack-dev-serverの新しいバージョンでは,contentBaseの代わりにstaticオプションを使用する必要がある.よって

webpack.config.js
...
module.exports = {
    ...
    devServer: {
        static: {
            directory: path.join(__dirname, "dist"),
        },
    },
    ...
};
so-heyso-hey

これでnpm run startをすると前回の最後と同じエラーが出たので,同じように修復する.以下をwebpack.config.jsに追加

webpack.config.js
...
module.exports = {
    ...
    experiments: {
        asyncWebAssembly: true,
    },
}
so-heyso-hey

まずCargo.tomlファイルの内容.(ここからはcargo generatenpm init rust-webpackの順で比較する)

Cargo.toml
[package]
name = "wasm-zenn-test"
version = "0.1.0"
authors = ["so-hey <soh.tngt@gmail.com>"]
edition = "2021"

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

[features]
default = ["console_error_panic_hook"]

[dependencies]
wasm-bindgen = "0.2.84"

# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.7", optional = true }

[dev-dependencies]
wasm-bindgen-test = "0.3.34"

[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"
Cargo.toml
# You must change these to your own details.
[package]
name = "rust-webpack-template"
description = "My super awesome Rust, WebAssembly, and Webpack project!"
version = "0.1.0"
authors = ["You <you@example.com>"]
categories = ["wasm"]
readme = "README.md"
edition = "2021"

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

[profile.release]
# This makes the compiled code faster and smaller, but it makes compiling slower,
# so it's only enabled in release mode.
lto = true

[features]
# If you uncomment this line, it will enable `wee_alloc`:
#default = ["wee_alloc"]

[dependencies]
# The `wasm-bindgen` crate provides the bare minimum functionality needed
# to interact with JavaScript.
wasm-bindgen = "0.2.45"

# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. However, it is slower than the default
# allocator, so it's not enabled by default.
wee_alloc = { version = "0.4.2", optional = true }

# The `web-sys` crate allows you to interact with the various browser APIs,
# like the DOM.
[dependencies.web-sys]
version = "0.3.22"
features = ["console"]

# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so it's only enabled
# in debug mode.
[target."cfg(debug_assertions)".dependencies]
console_error_panic_hook = "0.1.5"

# These crates are used for running unit tests.
[dev-dependencies]
wasm-bindgen-test = "0.2.45"
futures = "0.1.27"
js-sys = "0.3.22"
wasm-bindgen-futures = "0.3.22"
so-heyso-hey

[lib]について
"cdylib"はC Dynamic Libraryで「C互換の動的ライブラリ」を意味している.他の言語(特にC言語)からロードできる動的ライブラリを生成する.これは他の言語からRustのコードを使用するための一般的な方法である.
"rlib"はRust Libraryで「Rustライブラリ」を意味している.他のRustのコードから使用するための静的ライブラリを生成する.これはCargoが内部で使用する中間生成物とも言える.
このプロジェクトから生成されるライブラリのタイプを設定しているっぽい.

so-heyso-hey

[profile.release]はリリースビルドの設定を指定する.
lto = trueはリンク時最適化(Link Time Optimization)を有効にすることを指示している.LTOはコンパイラが生成するすべてのクレートを一緒に最適化する機能で,これにより生成されるコードがより高速かつ小さくなる.ただし,LTOを有効にするとコンパイル時間が長くなるため,通常はリリースモードでのみ有効にされるらしい.

so-heyso-hey

[features]はこのプロジェクトをライブラリとしたときのfeatureフラグについての記述をするところっぽい.
default = で設定したfeatureはパッケージがビルドされるときにデフォルトで有効になる.
console_error_panic_hookはRustのパニックフックの1つで,パニックメッセージをconsole.errorに転送する.
コメントアウトされているwee_allocは「Wasm-Enabled, Elfin Allocator」の略で,WebAssembly向けに設計されたメモリアロケータのこと.wee_allocは非常に小さなコードサイズを持つことを特徴としている.
ただ,コードサイズの小型化を目的としているので,デフォルトのグローバルアロケータなどに比べてパフォーマンスが劣る場合がある.そのため,動的なメモリ割り当てが頻繁に行われるような場面では,wee_allocの使用は適していないかもしれない.