この記事では、Viteを利用してAssemblyScriptの開発環境を整える手順を解説する。AssemblyScriptはWebAsseemblyにコンパイルされるプログラミング言語で、Webアプリケーションの開発サーバーを提供するViteと併用してスムーズに開発環境を整えることができる。ViteやAssemblyScript自体の詳細な解説はこの記事では行わない。

Viteを利用してプロジェクトを立ち上げる

まずは、Viteを利用してプロジェクトの雛形を作成する。以下のコマンドを実行することで作成できる。<project-name>は自由なプロジェクト名で置き換えてほしい。また、npmを使用するのでnode環境がインストールされていない場合は、自身の環境に合わせてインストールをする必要がある。

$ npm create vite@latest <project-name>
$ cd <project-name>
$ npm install

create時に幾つかの質問をされるので、インタラクティブに回答していく。ここでは、"select framework"には"Vanilla"、"select a variant"には"JavaScript"を指定することにする。例えば、Reactを使用したい場合にはそれをフレームワークとして指定することもできるし、TypeScriptを使いたければそのように指定することもできる。AssemblyScriptと組み合わせて使う上ではどれでも問題ないので好きなものを指定してほしい。(ただし、この記事ではReactやVue等との連携方法などについては検討していない。)

これで一先ずviteで開発用サーバーを立ち上げる準備が整った。以下のコマンドで開発サーバーを起動することができる。

$ npm run dev

初期設定では、http://localhost:5173 でアクセス可能だ。(ポート番号はViteの設定ファイルで変更することができる)。

AssemblyScriptの開発環境をセットアップ

続いて、viteプロジェクトにAssemblyScriptの開発環境を追加していく。まずは以下のコマンドでnpmでassemblyscriptをインストールする。

$ npm install --save-dev assemblyscript

次に、AssemblyScript用のディレクトリセットアップのために以下のコマンドを実行する。

$ npm asinit .

npxはローカルにインストールしたnpm packageのコマンドを実行するためのコマンドで、asinitはAssemblyScriptによって提供されるセットアップコマンドだ。

ディレクトリ構成の確認

ここまでの処理で、AssemblyScriptのインストールとディレクトリ作成が終わった。作成されたディレクトリを確認してみよう。AssemblyScriptやviteのバージョンによって異なるかもしれませんが、概ね以下のような構成になっているはずだ。

$ tree -L 1
.
├── asconfig.json
├── assembly
├── build
├── counter.js
├── index.html
├── javascript.svg
├── main.js
├── node_modules
├── package-lock.json
├── package.json
├── public
├── style.css
└── tests

assemblyディレクトリはAssemblyScriptのソースコードを配置するためのディレクトリで、buildディレクトリは(AssemblyScriptから)ビルドされたプログラムが出力されるディレクトリだ。(buildディレクトリは、後述のビルドコマンド使用時まで生成されないかもしれない)。
僕は普段ここに追加でjsディレクトリを作成してその配下にJavaScriptコードをまとめている。

ソースコードをビルドする

AssemblyScriptでは、コンパイラを使用してAssemblyScriptコードをWebAssemblyへコンパイルをして、それをJavaScriptからロードすることで使用できる。そのため、開発時にはコンパイルコマンドを実行する必要があります。以下のコマンドでコンパイル可能だ。

$ npm run asbuild

コンパイルされたコードは、JavaScriptからの呼び出し時に必要になるグルーコードなどと一緒にbuildディレクトリへ出力される。buildディレクトリの中身を確認してみよう。

$ tree build
build
├── debug.d.ts
├── debug.js
├── debug.wasm
├── debug.wasm.map
├── debug.wat
├── release.d.ts
├── release.js
├── release.wasm
├── release.wasm.map
└── release.wat

ビルドされたwasmコード(release.wasm)、それと対応するテキスト形式のwatコード(release.wat)、JavaScriptからAssemblyScriptのプログラム(コンパイルされたwasmプログラム)使用するために使用されるJavaScriptで記述されたグルーコード(release.js)等が出力されてる。

AssemblyScriptの関数をJavaScriptから呼び出す

最後に、実際の開発手順とAssemblyScriptコードの使用方法について説明する。AssemblyScriptコードはビルドすることでWebAssemblyのソースコードを出力する。通常、WebAssemblyのコードをJavaScriptで使用するには、WebAssembly.instantiate()を使用する。AssemblyScriptでは、このinstantiate関数の呼び出しの他にオブジェクトの参照カウントなどを行うコードを含んだグルーコード(release.js)が用意されているため、これをインポートするだけでAssemblyScriptコード(をコンパイルしたWebAssemblyコード)を使用することができる。

サンプルとして用意されているAssemblyScriptのコードを試しに使用してみよう。asinitによって以下のようなadd関数が定義されたファイルがassembly配下に出力されている。

export function add(a: i32, b: i32): i32 {
  return a + b;
}

i32型が使用されているのが特徴的な部分だ。このコードをJavaScriptから使用してみよう。まずは、上記のコードをWebAssemblyにコンパイルする。

$ npm run asbuild

続いて、コンパイルしたコードをJavaScriptから使用する。以下のように、build/release.jsをimportすることでAssemblyScript内でexportされた関数をJavaScriptの関数として使用することができる。

import { add } from "./build/release.js";

const x = add(1, 2);
console.log(x);

AssemblyScriptとJavaScriptはシンタックスが似ていることと、WebAssemblyコードをロードする部分は自分で書かずにラッパー(release.js)を使用することになるので混乱しやすいが、ホスト側がJavaScriptでゲスト側がAssemblyScript(からコンパイルされたWebAssembly)だ。