型を明示的に指定してコードがかけるので、最近は Kotlin を使うことが多い。 JSでも TypeScript で書けばよいのかもしれないが、もう一旦書いたコードを TypeScript で書き直すとか面倒すぎる。 そこで、Kotlin には Kotlin/JS があるので、これを使ってみた。
$ java -version
openjdk version "17.0.5" 2022-10-18
$ gradle -version
Gradle 7.4.2
$ npm -version
8.19.2
$ node --version
v18.12.0
プロジェクトディレクトリを作成:
$ mkdir hello-kjs
$ cd hello-kjs
$ touch build.gradle.kts
build.gradle.kts の内容:
plugins {
kotlin("js") version "1.7.22"
}
version = "0.1"
repositories {
mavenCentral()
}
kotlin {
js(LEGACY) {
browser {}
}
}
gradle wrapper しておきます。
$ gradle wrapper
これ以後は gradle コマンドの代わりに ./gradlew を使っていきます。
JavaSscript から使うための kotlin コードを書きます。 はじめに所定のディレクトリを作成して、空の Main.kt を用意。
$ mkdir -p src/main/kotlin
$ touch src/main/kotlin/Main.kt
Main.kt
fun greeting(name: String): String {
return "Hello, ${name}!"
}
それでは build します。
$ ./gradlew build
build/js/ に Node.js 用のプロジェクトができている。 そして今アプリケーション(といっても単に挨拶するだけの関数だが)である hello-kjs も build/js/node_modules/hello-kjs/ に配置されている。
それでは cd build/js/ してその中で この greeting 関数を使う JavaScript コードを書きます。
$ touch build/js/index.js
$ cd build/js
index.js
const helloKJS = require('hello-kjs');
console.log(helloKJS);
実行:
$ node index.js
{ 'greeting_61zpoe$': [Function: greeting] }
greeting 関数は helloKJS.greeting_61zpoe$ に割り当てられたようです.
index.js を書き換えて:
const helloKJS = require('hello-kjs');
const v = helloKJS.greeting_61zpoe$("World");
console.log(v);
実行:
$ node index.js
Hello, World!
kotlin で書いた greeting 関数を Node.js から使うことができました!
でもこの helloKJS.greeting_61zpoe$ って何?、気に入らない・・・と思って調べたら回避策ありました。
もう一度 hello-kjs プロジェクトに戻って、Main.kt を以下のように書き直します。
@OptIn(kotlin.js.ExperimentalJsExport::class)
@JsExport
fun greeting(name: String): String {
return "Hello, ${name}!"
}
@JsExport アノテーションを greeting 関数に追加しました。
@OptIn アノテーションも追加しました。こちらはなくても作動しますが、記述しないとコンパイラーが警告を出してくるので追加。
これで再度 build します。
$ ./gradlew build
build/js/ が更新されたので、再び build/js/ 以下に移動して node index.js します。
$ node index.js
const v = helloKJS.greeting_61zpoe$("World");
TypeError: helloKJS.greeting_61zpoe$ is not a function
helloKJS には greeting_61zpoe$ はないとのこと。 再度、require した helloKJS オブジェクトを調べてみると
// index.js
const helloKJS = require('hello-kjs');
console.log(helloKJS);
$ node index.js
{ greeting: [Function: greeting] }
やった。helloKJS.greeting で Main.kt で定義した greeting 関数が使えるようになっています。 早速 index.js を書き換えましょう。
// index.js
const helloKJS = require('hello-kjs');
const v = helloKJS.greeting("World");
console.log(v);
$ node index.js
Hello, World!
できた。
この helloKJS を index.html から使う場合は以下のようにします。
なおこれは Kotlin/JS 関係なく完全に Node.js (とWebpack) の世界の話です。
webpack を入れます。
$ npm install webpack --save-dev
$ npm install webpack-cli --save-dev
続いて、必要なファイルを作成します。
$ touch webpack.config.js
$ touch index.html
$ touch index.js
webpack.config.js, index.html, index.js はそれぞれ次の内容にします。
webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
entry: './index.js',
output: {
filename: 'helloKJS.js',
path: path.resolve(__dirname, 'dist')
}
};
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>KJS</title>
</head>
<body>
<div id="name">Kotlin</div>
<div id="message"></div>
<script src="dist/helloKJS.js"></script>
</body>
</html>
index.js
const helloKJS = require('hello-kjs');
const name = document.getElementById('name').textContent;
document.getElementById('message').textContent = helloKJS.greeting(name);
この段階でのプロジェクト構造の確認:
.
├── index.html
├── index.js
├── node_modules/
├── packages_imported/
├── webpack.config.js
└── yarn.lock
index.js を webpack してバンドルを ./dist/helloKJS.js に作成します。
$ npx webpack
この段階でのプロジェクト構造の確認:
.
├── dist
│ └── helloKJS.js
├── index.html
├── index.js
├── node_modules/
├── packages_imported/
├── webpack.config.js
└── yarn.lock
それでは index.html を開いて helloKJS.js 経由で Main.kt で定義した greeting 関数が使用できたか確認します。
意図通り作動しました。
なお当然ですが、もとの gradle プロジェクト hello-kjs で ./gradlew clean したら、ここまでの内容が全部消えるので、 成果物( ./build/js/ )は 必要ならどこかに移動しておきましょう。
Kotlin の standard library が全部 JS に変換できるのでしょうか。(まだ調べていない。) JSで書くより、Kotlin で書いた方が楽なことは Kotlin で書くことができる世界。 とはいえプロジェクト管理は(ここで把握したレベルでは)面倒。
Kotlin 側のコードで、java.lang.Math を使っている場合は kotlin.math に置き換えることで機能するようになります。
val a = 10
val b = 8
val v = Math.min(a,b)
これの Math.min 部分を以下に変更
val v = kotlin.math.min(a,b)