メモ2ブログ

メモtoウェブログ。旧ブログはこちら。 http://sakebook.blogspot.jp/

dukatでKotlin/JSを快適にする

前回、Kotlinで気持ちよくJSのライブラリを利用するには、モジュールを作成してあげる必要があると書きました。

sakebook.hatenablog.com

しかしこれをちゃんとやろうとすると、結構な手間になります。

定義して使い回しできるようにしている人がチラホラいます。

公式で出してくれれば揺らぎもなくてよいのになと思っていたら、公式からでてました。

github.com

dukat

d.tsの型定義ファイルからKotlinのコードを自動生成してくれます。

まだexperimentalですが、kotlin js pluginに含まれており、ビルドに組み込むことができます。

  • gradle.properties
kotlin.js.experimental.generateKotlinExternals=true
  • build.gradle.kts
plugins {
    kotlin("js") version "1.3.61"
}

repositories {
    jcenter()
}

dependencies {
    implementation(kotlin("stdlib-js"))
}

kotlin {
    sourceSets["main"].dependencies {
        // 利用したいライブラリを書く
    }
    target {
        nodejs {
            this.runTask {
                // バージョンを指定してあげる
                this.nodeJs.versions.dukat.version = "0.0.28"
            }
        }
    useCommonJs() // commonjs styleのJSを指定
    }
}

dukatは目下開発中のため、kotlin js pluginに組み込まれているバージョンは古いです。なので自分でバージョン指定を行い、最新バージョンにしてあげます。

次のコマンドでktファイルが自動生成されます。

$ ./gradlew generateExternals

生成されたktファイルは build/externals/${module}/src に配置されます。

ライブラリにd.tsファイルが同梱されているものしかktファイルは生成されません。ない場合は型定義ファイルも合わせて依存関係に追加すると使えます。

使ってみる(Plugin)

文字列操作ライブラリの voca に対して使ってみます。

github.com

型定義ファイルが別途用意されてるのでそちらも依存に加えます。こちらで検索できます。

  • build.gradle.kts
kotlin {
    sourceSets["main"].dependencies {
        implementation(npm("voca", "^1.4.0"))
        implementation(npm("@types/voca", "^1.4.0"))
    }
    ...
}

generateExternals タスクを実行すると今回は4つのktファイルが生成されます。

index.module_${library_name}kt ファイルにエントリーポイントやinterfaceなどが定義されています。ファイルの生成され方は型定義ファイルによって異なります。

今回利用する部分だけ抜粋します。

  • index.module_voca.kt
@JsModule("voca")
external val v: v.VocaStatic
  • index.v.module_voca.kt
@file:JsQualifier("v")
@file:Suppress("INTERFACE_WITH_SUPERCLASS", "OVERRIDING_FINAL_MEMBER", "RETURN_TYPE_MISMATCH_ON_OVERRIDE", "CONFLICTING_OVERLOADS", "EXTERNAL_DELEGATION")
package v
...
external interface Chain {
    ...
    fun chain(): ExplicitChain<String>
    ...
}
...
external interface ExplicitChain<T> {
    ...
    fun upperCase(): ExplicitChain<String>
    ...
    fun trim(whitespace: String = definedExternally): ExplicitChain<String>
    ...
}

このように定義されているので、次のように利用できます。

  • main.kt
fun main() {
    println("Hello Kotlin/${v.chain("  js  ").trim().upperCase()}")
}

v.IDEなどでサジェストが効くようになっています。たまんないですね。もちろん型情報もあります。

実行すると期待通りの変換がされます。

$ ./gradlew nodeRun

> Task :generateExternals UP-TO-DATE
tests

> Task :nodeRun
Hello Kotlin/JS

現状の問題点

今回のようにうまくいけばいいのですが、experimentalなだけあってまだまだ問題はたくさんあります。

ktファイルの生成に失敗するケースが結構あります。ktファイルの生成はできたがコンパイルができないケースもあります。

また、複数ライブラリを使う場合、全てのd.tsファイルを捜査して変換を試みるので、一つでも失敗するものがあるとktファイルが生成されません。

後者はdukatをPluginを使わずに利用することで回避できます。

使ってみる(CLI)

dukatをインストールします。利用できればglobalインストールじゃなくてもnpxでも構いません。

$ npm install -g dukat

プロジェクトルートにいることを想定して、対象の型定義ファイルとktファイルの展開先を指定します。

$ dukat -d src/main/external/ build/js/node_modules/\@types/voca/index.d.ts 
nonDeclarations.v.nonDeclarations.kt
index.module_voca.kt
index.v.module_voca.kt
lib.es2015.iterable.module_dukat.kt

src/main/external/ というpathにktファイルを展開させました。src/main/kotlin/ でも良いのですが、自動生成されたファイルと分けたいので別にしました。

src/main/external/ というフォルダはソースと認識されないので、ソースとして追加します。Pluginを使ってる場合は自動で build/externals/${module}/srcをソースに追加してくれてるので問題になりませんでした。

  • build.gradle.kts
kotlin {
    sourceSets["main"].kotlin.srcDir("src/main/external")
    sourceSets["main"].dependencies {
        implementation(npm("voca", "^1.4.0"))
        implementation(npm("@types/voca", "^1.4.0"))
    }
    target {
        nodejs()
        useCommonJs()
    }
}

これで実行してみます。gradle.propertiesの kotlin.js.experimental.generateKotlinExternals=true は削除するかコメントアウトしてください。

$ ./gradlew nodeRun

> Task :nodeRun
Hello Kotlin/JS

無事に実行できました。

まとめ

dukatがあるのでKotlin/JSの未来は明るいです。ですが明るい未来はまだ遠いので、手元で使えるライブラリを選定して部分的に利用していくのが良いです。

今回動かしたリポジトリはこちらです。

github.com

参考

TypeScript Types Search

Kotlin 1.3.50 released | Kotlin Blog

GitHub - Schahen/dukatGradleDemo

kotlin/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/dukat at master · JetBrains/kotlin · GitHub