本文へ移動

WebAssembly(WASI を使用) ​

WebAssembly は、安全でサンドボックス化されたポータブルなランタイムで、Web ブラウザーの内外で動作します。

具体的には、次のように使います。

  • JavaScript などの言語を WebAssembly(.wasm ファイル)にコンパイルします。
  • wasmtime や jco などの WebAssembly ランタイムで、WebAssembly バイナリを実行できます。

WebAssembly のコア自体は、ローカルファイルシステムやソケットなどにアクセスできませんが、WebAssembly System Interface が WebAssembly のワークロードが利用する基盤のプラットフォームを定義できるようにします。

つまり、WASI を使うことで、WebAssembly はファイルやソケットなどを操作できます。

情報

WASI インターフェースを確認したい場合は、wasi:http を参照してください。

JavaScript で WASI を使う WebAssembly のサポートは StarlingMonkey が提供します。 StarlingMonkey と Hono はともに Web 標準を重視しているため、Hono は WASI 対応の WebAssembly エコシステムで、そのまま動作します*。

1. セットアップ ​

WebAssembly の JavaScript エコシステムには、WASI 対応の WebAssembly コンポーネントを簡単に構築し始めるためのツールがあります。

  • StarlingMonkey は SpiderMonkey のフォークで、WebAssembly にコンパイルしてコンポーネントを利用できるようにします。
  • componentize-js は JavaScript の ES モジュールを WebAssembly コンポーネントに変換します。
  • jco はコンポーネントのビルド、型の生成、Node.js やブラウザーなどでのコンポーネントの実行ができる多機能ツールです。

情報

WebAssembly は開かれたエコシステムを持つオープンソースで、主要なプロジェクトは主に Bytecode Alliance とそのメンバーが管理しています。

新機能、問題報告、プルリクエストなど、あらゆる貢献を歓迎しています。

WebAssembly 上で Hono を使うスターターテンプレートはまだありませんが、他のプロジェクトと同様に開始できます。

sh
mkdir my-app
cd my-app
npm init
npm i hono
npm i -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
npm i -D rolldown
sh
mkdir my-app
cd my-app
npm init
yarn add hono
yarn add -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
yarn add -D rolldown
sh
mkdir my-app
cd my-app
pnpm init --init-type module
pnpm add hono
pnpm add -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
pnpm add -D rolldown
sh
mkdir my-app
cd my-app
npm init
bun add hono
bun add -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std

情報

プロジェクトで ES モジュールを使うには、package.json の type を "module" に設定してください。

my-app フォルダーに移動したら、依存関係をインストールし、TypeScript を初期化します。

sh
npm i
npx tsc --init
sh
yarn
yarn tsc --init
sh
pnpm i
pnpm exec tsc --init
sh
bun i

基本的な TypeScript の設定ファイル(tsconfig.json)を作成したら、次の設定を確認してください。

  • compilerOptions.module を "nodenext" に設定します。

componentize-js(およびそれを利用する jco)は単一の JavaScript ファイルのみをサポートするため、 バンドルが必要です。rolldown を使うと、単一ファイルのバンドルを作成できます。

次の Rolldown 設定(rolldown.config.mjs)を利用できます。

js
import { defineConfig } from 'rolldown'

export default defineConfig({
  input: 'src/component.ts',
  external: /wasi:.*/,
  output: {
    file: 'dist/component.js',
    format: 'esm',
  },
})

情報

使い慣れた他のバンドラー(rolldown、esbuild、rollup など)を使っても構いません。

2. WIT インターフェースと依存関係のセットアップ ​

WebAssembly Interface Types(WIT) は、WebAssembly コンポーネントが使う機能(インポート)と提供する機能(エクスポート)を定めるインターフェース定義言語(IDL)です。

標準化された WIT インターフェースのうち、wasi:http は HTTP リクエストの送受信を扱います。Web サーバーを作成するため、コンポーネントは WIT world で wasi:http/incoming-handler の使用を宣言する必要があります。

まず、wit/component.wit というファイルにコンポーネントの WIT world を設定します。

txt
package example:hono;

world component {
    export wasi:http/incoming-handler@0.2.6;
}

簡単に言えば、この WIT ファイルは、コンポーネントが HTTP リクエストの受信と処理の機能を「提供する」ことを意味します。

wasi:http/incoming-handler インターフェースは、リクエストの構造などを定義する上流の標準化された WIT インターフェースに依存します。

Bytecode Alliance が管理するこれらのサードパーティの WIT インターフェースを取得するには、wkg を使えます。

sh
wkg wit fetch

wkg の実行が完了すると、wit フォルダー内の component.wit と同じ階層に、新しい deps フォルダーが作成されます。

wit
├── component.wit
└── deps
    ├── wasi-cli-0.2.6
    │   └── package.wit
    ├── wasi-clocks-0.2.6
    │   └── package.wit
    ├── wasi-http-0.2.6
    │   └── package.wit
    ├── wasi-io-0.2.6
    │   └── package.wit
    └── wasi-random-0.2.6
        └── package.wit

3. Hello Wasm ​

WebAssembly で HTTP サーバーを作るには、[jco-std][jco-std] プロジェクトを利用できます。通常の Hono の開発体験に近づけるヘルパーが用意されています。

src/component.ts というファイルで、基本的な Hono アプリケーションを WebAssembly コンポーネントとして作成し、component world を実装します。

ts
import { Hono } from 'hono'
import { fire } from '@bytecodealliance/jco-std/wasi/0.2.6/http/adapters/hono/server'

const app = new Hono()

app.get('/hello', (c) => {
  return c.json({ message: 'Hello from WebAssembly!' })
})

fire(app)

// Although we've called `fire()` with wasi HTTP configured for use above,
// we still need to actually export the `wasi:http/incoming-handler` interface object,
// as jco and componentize-js will be looking for the ES module export that matches the WASI interface.
export { incomingHandler } from '@bytecodealliance/jco-std/wasi/0.2.6/http/adapters/hono/server'

4. ビルド ​

Rolldown を使い、TypeScript のコンパイルにも対応するよう設定しているため、これでビルドとバンドルを行えます。

sh
npx rolldown -c
sh
yarn rolldown -c
sh
pnpm exec rolldown -c
sh
bun build --target=bun --outfile=dist/component.js ./src/component.ts

情報

WebAssembly の JavaScript ツールは現在単一の JavaScript ファイルのみをサポートしており、Hono と関連ライブラリーを含めたいので、バンドルが必要です。

要件がより単純なコンポーネントでは、バンドラーは不要です。

jco(間接的に componentize-js)を使って WebAssembly コンポーネントをビルドします。

sh
npx jco componentize -w wit -o dist/component.wasm dist/component.js
sh
yarn jco componentize -w wit -o dist/component.wasm dist/component.js
sh
pnpm exec jco componentize -w wit -o dist/component.wasm dist/component.js
sh
bun run jco componentize -w wit -o dist/component.wasm dist/component.js

5. 実行 ​

Hono の WebAssembly HTTP サーバーを実行するには、WASI 対応の任意の WebAssembly ランタイムを使えます。

このガイドでは、すでにインストールされている jco serve を使います。

注意

jco serve は開発用で、本番環境での利用は推奨されません。

sh
npx jco serve dist/component.wasm
sh
yarn jco serve dist/component.wasm
sh
pnpm exec jco serve dist/component.wasm
sh
bun run jco serve dist/component.wasm

次のような出力が表示されます。

$ npx jco serve dist/component.wasm
Server listening @ localhost:8000...

localhost:8000/hello にリクエストを送ると、Hono アプリケーションで指定した JSON が出力されます。

次のような出力が表示されます。

json
{ "message": "Hello from WebAssembly!" }

情報

jco serve は、WebAssembly コンポーネントを基本的な WebAssembly コアモジュールに変換し、Node.js やブラウザーなどのランタイムで実行できるようにします。

この処理は通常 jco transpile で実行します。これにより、Node.js やブラウザーなど(V8 などの JavaScript エンジンを使う)の環境を WebAssembly コンポーネントのランタイムとして利用できます。

jco transpile の仕組みはこのガイドの範囲外です。詳しくは Jco ブックを参照してください。

詳細情報 ​

WASI や WebAssembly コンポーネントなどについて詳しく知るには、次の資料を参照してください。

WebAssembly コミュニティへの質問、コメント、貢献、問題報告には、次を利用できます。

MIT ライセンスで公開されています。