Home About Contact
Kotlin , Kotlin Multiplatform , JavaScript , Node.js , webpack

Kotlin Multiplatform 2.0.0 Kotlin/JS, webpack で sayHello するライブラリをつくるところまで

ちょっと前に Kotlin Multiplatform 1.9.22 Kotlin/JS Hello, World! を書いたのですが、2.0.0 が出たのでやりなおしです。

このあたりの https://kotlinlang.org/docs/js-project-setup.html 話です。

環境

$ java --version
openjdk version "17.0.11" 2024-04-16

$ gradle --version
Gradle 8.5

$ npm --version
10.4.0

$ node --version
v18.17.1

hello プロジェクト

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

$ mkdir hello
$ cd hello
$ touch build.gradle.kts 

build.gradle.kts の内容:

plugins {
    kotlin("multiplatform") version "2.0.0"
}

version = "0.1"

repositories {
    mavenCentral()
}

kotlin {
    js {
        nodejs()
        binaries.library()
    }
}

続いて Main.kt の用意:

$ mkdir -p src/commonMain/kotlin/
$ touch src/commonMain/kotlin/Main.kt

Main.kt を配置するパスは src/commonMain/kotlin の代わりに src/jsMain/kotlin にしても問題ありませんでした。

Main.kt にコードを記述。

@ExperimentalJsExport
@JsExport
fun sayHello(name: String): String {
    return "Hello, ${name}!"
}

@ExperimentalJsExport アノテーションをつけないでビルドするとビルド時になんやかんやいわれるのでつけた方が良さそう(つけなくても作動するにはするようですが)。

ここで、一度 build して問題がないか確かめます。

$ gradle build

ビルドできたら、 続いて gradle wrapper しておきます。

$ gradle wrapper

これ以後は gradle コマンドの代わりに ./gradlew を使います。

gradlew が機能するか確かめるために clean と build を実行。

$ ./gradlew clean build

ビルドした hello.js がどこに生成されたか確認。

$ find . -name hello.js
./build/js/packages/hello/kotlin/hello.js
./build/dist/js/productionLibrary/hello.js
./build/compileSync/js/main/productionLibrary/kotlin/hello.js

よくわからないのだが、たぶん、build/js/packages/hello/kotlin/hello.js を使えばいいのではないか?

hello.js は次の内容で生成されています。

(function (root, factory) {
  if (typeof define === 'function' && define.amd)
    define(['exports'], factory);
  else if (typeof exports === 'object')
    factory(module.exports);
  else
    root.hello = factory(typeof hello === 'undefined' ? {} : hello);
}(globalThis, function (_) {
  'use strict';
  //region block: pre-declaration
  //endregion
  function sayHello(name) {
    return 'Hello, ' + name + '!';
  }
  //region block: exports
  function $jsExportAll$(_) {
    _.sayHello = sayHello;
  }
  $jsExportAll$(_);
  //endregion
  return _;
}));

//# sourceMappingURL=hello.js.map

たしかに sayHello 関数が入っています。よさそう。

このライブラリの sayHello 関数を Javascript (Node.js) から使うには、次のようにします。

$ cd build/js
$ touch main.js

./build ディレクトリは ./gradlew clean したら消えるので注意しましょう。必要なら build/js ごとどこかにコピーして作業すべきかもしれません。

まず CommonJS 方式で使ってみます。

main.js:

const hello = require("hello");
console.log(hello);
console.log(hello.sayHello("Pika"));

node main.js として実行して作動を確かめます。

$ node main.js
{ sayHello: [Function: sayHello] }
Hello, Pika!

うまくいきました。

ESModule としても使えます。

$ touch main.mjs

main.mjs に次のコードを書きます。

import hello from "hello";
console.log(hello);
console.log(hello.sayHello("Pika"));

node main.mjs として実行すれば先ほどと同じ結果が得られます。

これで sayHello するライブラリはできました。

しかし、現状のままでは main.js (または main.mjs) を動かすためには、 ./node_modules/ , ./packages/ を全部コピーする必要があります。

そこで webpack を使って このライブラリを一つのファイルにまとめます。

webpack

./gradlew build すると生成される ./build/js/ ディレクトリは package.json があるのを見てもわかるようにすでに Node.js のプロジェクトになっています。 webpack してファイルを一つにまとめたいので、webpack コマンドが使えるように webpack と webpack-cli を入れます。

$ npm install webpack@5.92.0 --save-dev
$ npm install webpack-cli@5.1.4 --save-dev

続いて webpack.config.js を用意します。

const path = require('path');

module.exports = {
    mode: 'production',
    entry: './packages/hello/kotlin/hello.js',
    output: {
        library: 'hello',
        libraryTarget: 'commonjs',
        path: __dirname,
        filename: 'hellolib.js'
    }
}

そして、webpack します。

$ npx webpack --config webpack.config.js

これでカレントディレクトリに hellolib.js が生成されます。

webpack.config.js の module.exports.output.filename に指定したファイル名で生成される。

index.js を用意して次のように記述します。

const hello = require("./hellolib.js").hello;
console.log(hello);
console.log(hello.sayHello("Pika"));

実行してみます。

$ node index.js
{ sayHello: [Function: o] }
Hello, Pika!

先ほどと同じように作動しました。

これで index.jshellolib.js の2つのファイルだけを配布すれば このライブラリを作動させることができるようになりました。

以上です。