Kotlin/Wasm(+React)でHello, World!してみる
寒くなってきましたね。筑波大学情報メディア創成学類3年のちゅるりです。Zenn は初投稿となります、よろしくおねがいします〜〜〜!
10月31日にリリースされた Chrome 119 で、WebAssembly GC(Garbage Collection)がついに正式機能になった[1]のを記念して、Kotlin/Wasm で Hello, World! してみたので、そのチュートリアルを残しておこうと思います。
Kotlin/Wasmとは
今まで、 Kotlin は主に Android アプリの開発言語として利用されてきました。しかし近年、JetBrains は Kotlin のマルチプラットフォーム言語戦略として、Kotlin/JVM を皮切りに、Kotlin/JS、Kotlin/Native といった様々なコンパイル・ターゲットを提供してきました。その一つとして Kotlin/Wasm があります。Kotlin/Wasm は、その名前からも類推されるように、Wasm(WebAssembly)を対象としたコンパイル・ターゲットです。Kotlin 1.9.20 にて Kotlin/Wasm のビルド済みの Wasm バイナリのサイズが、初期バージョンのときから10分の1となる[2]など、最近は活発に開発が進んでいるようです。
また、Chrome 119 で Wasm GC が正式機能として実装されたことにより、高級言語による Wasm 開発への門戸が開かれました。今までは、C++ や Rust、Go のような、主に低レイヤの実装でよく使われる言語によるWasm 開発が主流でした。従って、こういった言語には馴染みのない Web エンジニアの方々も多く(自分もそうでした)Wasm 開発の敷居は高いままでした。Wasm GC の実装により、Kotlin をはじめとして今後はより多くの言語で Wasm 開発が可能となることでしょう。
チュートリアル
今回は、Kotlin/Wasm で Hello, World!
と出力するだけの Node.js ライブラリを作り、それを React アプリから読み込むという方法で Kotlin/Wasm を体験していきたいと思います。
環境
- IntelliJ IDEA Ultimate 2023.1.2
- (WebStorm 2023.1.2)
- Google Chrome 119.x
- Node.js v18.15.0
WebStorm は無くても問題ありませんが、Kotlin を使用する都合で、IntelliJ はあるといいと思います。インストールは済ませておいてください。
下準備
Wasm GCの有効化
Chrome 119 で Wasm GC が正式機能になったとはいえ、GC フラグの有効化が必要なようです。
-
chrome://flags
を開く -
gc
と検索し、WebAssembly Garbage Collection
をEnabled
にする - Google Chrome を再起動する
Wasmライブラリを作る
見本のプロジェクトを GitHub に用意したので適宜参考にしてください!
プロジェクトの作成
次のようにプロパティを設定し、CREATE
をクリックしてください。
- Name:
kotlin-wasm-example
- Language:
kotlin
- Build System:
Gradle
- Gradle DSL:
Kotlin
build.gradle.ktsの設定
GitHubのkotlin-wasm-exampleの build.gradle.kts
の内容を自分の build.gradle.kts
にコピペします。エラーが出るかと思いますが、一度無視して構いません。
src/mainのリネーム
src/main
ディレクトリを src/wasmJsMain
とリネームします。
プロジェクトのリロード
build.gradle.kts
を変更したので、プロジェクト全体をリロードする必要があります。右側の Gradle
タブを開いて、Gradle
という文字列直下のリロードボタン(丸い矢印)をクリックしてください。リロードにはしばらく時間がかかります。リロードが終了すると、build.gradle.kts
のエラーが消えているかと思います。
Main.ktの作成
いよいよ実行対象となる Kotlin プログラムを作成します。src/wasmJsMain/kotlin
配下に Main.kt
という名前で Kotlin ファイルを作成してください。作成したのち、次のプログラムを書き込みます。
@JsExport
アノテーションを使用することで、Kotlin 関数を JavaScript 側にエクスポートすることができるようです[3]。また、main
関数はこの Wasm モジュールが読み込まれた際に実行されるようです。
fun main() {
println("Loaded!")
}
@JsExport
fun hello() {
println("Hello, World!")
}
プロジェクトのビルド
右側の Gradle
タブの kotlin node/wasmJsNodeRun
を実行し、Wasm へのコンパイルとNode.js ライブラリの生成を行います。しばらくの時間の後、左側のファイルツリーに build
ディレクトリが生成されると思います。
npmパッケージをリンクする
続いてターミナルを開き、生成された Node.js ライブラリをnpmを通してリンクします(npm link
についてはこちら)
$ cd build/js
$ npm link
これで、Kotlin/Wasm の Node.js ライブラリの作成は終わりです。めちゃくちゃ手軽ですね。
Kotlin/Wasmを使用するReactアプリを作る
Reactアプリを作成する
僕は JetBrains 系 IDE をよく使用するので WebStorm を使用します。適当な名前でプロジェクトを作成してください。
webpackDevServer.config.jsを設定する
本来はこの手順を飛ばしてもよいはずですが、現状このまま進むと *.wasm
ファイルを fetch したときに Invalid MIMEtype
エラーが発生してしまってうまく動かないので、これを抑制します。
node_modules/react_scripts/config/webpackDevServer.config.js
を開き、onBeforeSetupMiddleware(devServer)
関数内に次のコードを追加してください。
devServer.app.get('*.wasm', function (req, res, next) {
var options = {
root: `${__dirname}/../../kotlin-wasm-example/packages/kotlin-wasm-example-wasm-js/kotlin`,
dotfiles: 'deny',
headers: {
'Content-Type': 'application/wasm'
}
};
res.sendFile(req.url, options, function (err) {
if (err) {
next(err);
}
});
});
以上の抑制方法は開発環境で動かすこと前提であり、特に node_modules
を改変しているという点で本来はあまりすべきではありません。しかし、今回は Kotlin/Wasm をいち早く体験するというために、このような処置を実行しています。ほかの対応方法を検討する場合、application/wasm MIMEtype問題を解決しつつ開発サーバを建てる方法等の記事が参考になるかと思います。
作成したパッケージをリンクする
プロジェクトのルートディレクトリで次のコマンドを実行し、先程作成したNode.jsライブラリをリンクします。
$ npm link kotlin-wasm-example
App.jsxでWasmの読み込みを行う
App.jsxに次のコードを挿入してください。今回はこれだけで大丈夫です。
// 挿入
import wasmModule from 'kotlin-wasm-example/packages/kotlin-wasm-example-wasm-js/kotlin/kotlin-wasm-example-wasm-js.mjs';
import {useEffect} from "react";
function App() {
// 挿入
useEffect(() => {
wasmModule.hello()
}, []);
// 挿入ここまで
return // ...
}
実行する
npm run start
を実行し、開発サーバを立ててブラウザでアクセスしてみてください。うまく行っていれば、コンソールに Hello, World!
が表示されているかと思います...おめでとうございます!これで Hello, World
が完了しました!
!
さいごに
今回は Kotlin/Wasm で Hello, World!
をする方法をご紹介しました。Kotlin で Wasm を開発できるようになったのはとてもおもしろく、Web 開発の幅がまた広がったように感じます。僕自身も今回はじめて Kotlin/Wasm に触ってみたので、今後も得られた知見などを随時まとめていきたいと思います。最後までお読みいただきありがとうございました。
Discussion