Next.js+Rust+WebAssembly+Marpでスライドを自動生成するwebアプリを作る
まずは
npx create-next-app@latest project-name --use-npm
を実行する.
PS C:\Users\my-name\Desktop> npx create-next-app@latest project-name --use-npm
√ Would you like to use TypeScript? ... Yes
√ Would you like to use ESLint? ... Yes
√ Would you like to use Tailwind CSS? ... Yes
√ Would you like to use `src/` directory? ... Yes
√ Would you like to use App Router? (recommended) ... Yes
√ Would you like to customize the default import alias (@/*)? ... No
Creating a new Next.js app in C:\Users\my-name\Desktop\project-name.
project-name
に移動して,早速
npm run build
でビルドして,
npm run start
でアプリを起動.-> 問題なし
ここで,Rust+WebAssemblyを導入.
cargo new directory-name --lib
cd directory-name
wasm-pack build --target web
を実行.(ここですでにwasm-packはインストールしているので,それ関連のエラーはなし)
しかし,
Error: crate-type must be cdylib to compile to wasm32-unknown-unknown. Add the following to your Cargo.toml file:
[lib]
crate-type = ["cdylib", "rlib"]
Caused by: crate-type must be cdylib to compile to wasm32-unknown-unknown. Add the following to your Cargo.toml file:
[lib]
crate-type = ["cdylib", "rlib"]
というエラーが出た.
これはCargo.toml
に
[lib]
crate-type = ["cdylib", "rlib"]
を書けば解決.再びwasm-pack build --target web
を実行すると
[INFO]: 🎯 Checking for the Wasm target...
[INFO]: 🌀 Compiling to Wasm...
Compiling backend v0.1.0 (C:\Users\my-name\Desktop\project-name\directory-name)
Finished release [optimized] target(s) in 0.14s
Error: Ensure that you have "wasm-bindgen" as a dependency in your Cargo.toml file:
[dependencies]
wasm-bindgen = "0.2"
Caused by: Ensure that you have "wasm-bindgen" as a dependency in your Cargo.toml file:
[dependencies]
wasm-bindgen = "0.2"
またエラー.
これもCargo.toml
に
[dependencies]
wasm-bindgen = "0.2"
を追加するか,cargo add wasm-bindgen
を実行することで解決.
またまたwasm-pack build --target web
を実行.
どこで何が起こるのかわからないので,こまめにnpm run build
しておく.
今の時点では問題なし.今後も頻繁に確認する.
next.jsのプロジェクトにWebAssemblyモジュールを統合する.これにより,フロントエンドからRustの関数を呼び出すことができる.
まず,必要なパッケージをインストールする.
npm install @wasm-tool/wasm-pack-plugin webpack
これは問題なくいける.
次に,webpackの設定を追加する.next.config.mjs
を以下の内容に書き換える.
import path from "path";
import WasmPackPlugin from "@wasm-tool/wasm-pack-plugin";
import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const nextConfig = {
webpack: (config, {}) => {
config.plugins.push(
new WasmPackPlugin({
crateDirectory: path.resolve(__dirname, "./directory-name"),
outDir: path.resolve(__dirname, "./public/pkg"),
outName: "rust_wasm",
})
);
config.experiments = {
syncWebAssembly: true,
layers: true,
};
return config;
},
};
export default nextConfig;
注意してほしいのが,これは .mjs
ファイルであることだ.(ここによると,v14.1あたりからデフォルトで.mjs
になっているらしい.)
この状態でnpm run build
を実行すると,project-name/public/pkg
にWebAssmblyモジュールが出力されるので,最初にビルドして出力したproject-name/directory-name/pkg
は消しちゃう.
これでたぶん大体のセットアップは完了したはず.
project-name/directory-name/src/lib.rs
を以下の内容に書き換える.
use wasm_bindgen::prelude::*;
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(a: &str);
}
macro_rules! console_log {
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
console_log!("Hello, {}", name);
format!("Hello, {}!", name)
}
#[wasm_bindgen]
pub fn add(a: isize, b: isize) -> isize {
console_log!("called add function");
a + b
}
さらにproject-name/src/app/page.tsx
を以下の内容に書き換える.
"use client";
import { useEffect, useState } from "react";
import initSync, { greet, add } from "../../public/pkg";
export default function Home() {
const [greeting, setGreeting] = useState("now loading...");
const [num, setNum] = useState(0);
const greetAndAdd = () => {
const greeting = greet("World");
setGreeting(greeting);
setNum((num) => add(num, 1));
};
useEffect(() => {
initSync();
}, []);
return (
<center>
<h1>{greeting}</h1>
<h1>{num}</h1>
<button onClick={greetAndAdd}>greet and add button</button>
</center>
);
}
これでnpm run build
からnpm run start
を実行すると,画面の左上に
Hello, World!
0
greet and add button
となっていて,ボタンを押すと数字が増える.
ここまでに多分8時間くらいかかってる...
ついでに,project-name/package.json
に
{
...
"dependencies": {
...
"file-name": "file:public/pkg"
}
...
}
と追加する.その後npm install file-name
を実行すると,"file-name"をモジュール名として使用することができる.
npm install file-name
で
1 moderate severity vulnerability
To address all issues, run:
npm audit fix
Run `npm audit` for details.
のエラーが出たら,素直にnpm audit fix
して対処する.