Home About
Kotlin , JavaScript , Node.js

Kotlin/JS Hello, World! Kotlin のコードを Node.js で使う

型を明示的に指定してコードがかけるので、最近は 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

hello-kjs プロジェクト

プロジェクトディレクトリを作成:

$ 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 を HTML で使う

この 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 関数が使用できたか確認します。

hello KJS

意図通り作動しました。

なお当然ですが、もとの gradle プロジェクト hello-kjs で ./gradlew clean したら、ここまでの内容が全部消えるので、 成果物( ./build/js/ )は 必要ならどこかに移動しておきましょう。

まとめ

Kotlin の standard library が全部 JS に変換できるのでしょうか。(まだ調べていない。) JSで書くより、Kotlin で書いた方が楽なことは Kotlin で書くことができる世界。 とはいえプロジェクト管理は(ここで把握したレベルでは)面倒。

java.lang.Math

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)

Liked some of this entry? Buy me a coffee, please.