LoginSignup
12
4

More than 3 years have passed since last update.

GASをKotlin/JSで書こう!

Last updated at Posted at 2020-09-10

GASをKotlinで書きたい!

皆さんはGASとKotlinを利用されたことはありますか?
この両方を使ったことがある方であれば、「GASをKotlinで書けたら良いのにな~」と考えたことがあるかと思います。
私自身もそう思い、色々トライしていたのですが、Kotlin/JSの出力があまりに巨大で、GASでの利用を諦めていました。
しかし、最近リリースされたKotlin 1.4の新しいIRコンパイラを試してみると、これが非常に優秀で、出力が非常に小さくなっていることが分かりました。
過去にぶつかってしまった課題も解決したので、改めて挑戦したところ、なかなかいい感じに動作させることに成功しましたので、ここに共有します。

注意

「GASをKotlinで書きたい!」の項で触れている通り、この記事では執筆時点でalpha版な新しいKotlin/JS IRコンパイラを利用します。今後、この記事の内容が正しくなくなる可能性も高いのでご注意ください。

サンプルコード

こちらに既に動作を確認したサンプルコードを用意しました。
https://github.com/5hyn3/GasByKotlin

GASをKotlinで動かしてみる

実際にGASをKotlinで動かしてみましょう!

環境の構築

以下の前提があります。

  • WSL2上のUbuntu 20.04 LTSで作業を行っています
  • IntelliJ IDEA Ultimate 2020.2を利用します
  • Kotlin 1.4.0で動作確認を行っています
  • node(v12.18.1), npm(v6.14.5)が導入済み

1. プロジェクトの作成

以下の画像のように、Kotlin/JS for browserを選択してプロジェクトを作成します
a4bc6231ea6d38ed55196eb6eb766f49.png

2. gradle.propertiesの編集

新しいKotlin/JS IRコンパイラを利用するので、以下をgradle.propertiesに追記します

kotlin.js.compiler=ir

3. build.gradleの編集

プロジェクトを生成したら build.gradleを以下のように編集します

build.gradle
plugins {
    id 'org.jetbrains.kotlin.js' version '1.4.0'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-js"
    testImplementation "org.jetbrains.kotlin:kotlin-test-js"
}

kotlin {
    js {
        browser {
            webpackTask {
                output.libraryTarget = "commonjs"
            }

            testTask {
                useKarma {
                    useChromeHeadless()
                    webpackConfig.cssSupport.enabled = true
                }
            }
        }
        binaries.executable()
    }
}

ここで大事なポイントが

            webpackTask {
                output.libraryTarget = "commonjs"
            }

の部分です。これが本記事で最も重要なポイントであると言っても過言ではありません。Kotlin/JSは出力するJavaScriptの形式を大まかにブラウザ向けnode向けのどちらかで選択することができます。GASで動かしたいJavaScriptを出力するには、node向けを選択する必要があるのですが、新しいKotlin/JS IRコンパイラは、ブラウザ向けの場合にのみminifyを行います。node向けを選択するとminifyを行ってくれず、相変わらず巨大なソースコードが出力されることになります。しかし、ブラウザ向けの設定とすると、windowなど、ブラウザ依存のコードが出力されてしまい、GASで動作しないコードが出力されるようになってしまいます。
そこで、上記のように DSLでbrowserを指定しつつも、出力の設定をcommonjsとすることで、minifyを有効にしつつ、ブラウザ依存のコードを出力しないように設定しています。

4. kotlinのコードを書く

src/main/kotlin/main.ktに適当なコードを書きます。
このドキュメントでは以下のようなコードを書いた体で進めます。

main.kt
@JsExport
fun Hello() = "Hello"

JavaScript側に公開したいメソッドには@JsExportをつけてください。
つけ忘れるとビルドした際に利用されていないコードとして出力から削除されてしまいます。

5. ビルドする

./gradlew buildでも、IntelliJ右上のビルドボタンでもどちらでも良いのでビルドを行います。
ビルド成功後、build/distributions内に、${プロジェクトの名前}.jsファイルが出力されていることを確認してください。

6. グルーコードの作成

GASはGlobalなファンクションをエントリポイントとして動作しますが、Kotlin/JSではJavaScriptの世界におけるGlobalなファンクションを定義することは今のところできません。そのため、100%Kotlin/JSだけでGASを動作させることは今の所不可能で、GASとKotlin/JSを繋げるJavaScriptによるグルーコードが必要となります。例えば、GAS側にmyFunctionという名前の関数を認識させて実行し、Kotlin/JSのコードの実行結果をログに保存させたい場合は以下のようになります。以下のコードはGasByKotlinという名前のプロジェクトの場合です。

main.js
var GasByKotlin = require('../build/distributions/GasByKotlin'); //ここは5で確認したビルドによって出力されたファイルを指定する

global.myFunction = function() { // メソッドはこの形式で定義しないとGASから認識されません
    console.log(GasByKotlin.GasByKotlin.hello()) // JavaScriptでの変数名.${Kotlin側のプロジェクトファイルの名前}.${メソッド名}()でKotlin側のコードを呼び出せます
}

7. npmパッケージへの依存を追加する

これでコードの方はほぼ完成です。但し、当然このままではKotlin/JSのコードをGASに上げることはできないので、npmのパッケージへの依存を追加して、コードの変換やGASへのpushを行えるようにします。
build.gradleのdependenciesに以下を追記してください。

build.gradle
dependencies {
    ...
    implementation npm("@google/clasp", "2.3.0") // GASのプロジェクトを作ったりpushしたりしてくれるGoogle公式パッケージ
    implementation npm("browserify", "16.5.2") // 以下のgasifyと合わせて、Kotlin/JSのコードをGASで動作するように変換させるために利用します
    implementation npm("gasify", "1.0.1") 
}

追記後 ./gradlew kotlinNpmInstallを実行することで、build/js/node_modulesにnpmパッケージがインストールされます。

8. claspのセットアップ

ビルドがうまくいったらプロジェクトのルートディレクトリでrootDirの設定をしつつclaspのセットアップをして適当なプロジェクトにpush出来る状態にしましょう。
7でclaspをbuild/js/node_modulesにインストールしたため、build/js/node_modules/.bin/claspでclaspを実行することができます。
claspについての解説は、他で取り扱っている記事がたくさんあるため、ここでは割愛します。

9. GAS向けにJSを変換する

現状のKotlin/JSによって出力されたJavaScriptにはexportが使われており、そのままではGASで利用することができません。そこで、browserifyとgasifyを利用して、このコードをGASでも動くように変換します。

build/js/node_modules/.bin/browserify ${6で作ったJavaScript} -p build/js/node_modules/gasify -o ${8で設定したclaspのrootDir}/{適当な名前}.js

10. pushする

build/js/node_modules/.bin/clasp push

11. 動作確認

GASのエディタを開いて、実際に動作を確認してみましょう。うまくいけば以下の画像のように7で作ったJavaScriptのメソッドが実行できるようになっており、実際に動作することを確認できます。
見ての通り、(横には長いですが)十行と少し程度のコンパクトなサイズにまとまっています。1

image.png

12. ビルドをもうちょっと楽にする

Kotlinのコードを書いてから8, 9の操作を毎回行ってGASにコードを反映させる作業は面倒なため、gradleのタスクを定義してコマンド一つでビルドからpushまでを行ってくれるようにします。
以下をbuild.gradleに追記してください。

build.gradle

task convertForGas(type: Exec) { //9で行った作業を定義しています
    executable "$buildDir/js/node_modules/.bin/browserify"
    args "${6で作ったJavaScript}", "-p", "$buildDir/js/node_modules/gasify", "-o", "${8で設定したclaspのrootDir}/{適当な名前}.js"
}

task push(type: Exec) { // 10で行った作業を定義しています
    executable "$buildDir/js/node_modules/.bin/clasp"
    args "push"
}

task buildAndPush {
    dependsOn build
    dependsOn convertForGas
    dependsOn push
}

このようにタスク定義をしておくことで、Kotlinのコードを変更するたびに

./gradlew buildAndPush

とコマンドを一つ実行するだけでKotlinのビルド、コードの変換、claspによるpushが行われるようになります。

13. Enjoy Kotlin!

ここまでできればKotlin/JSでGASのコードを実装する環境が整っています!どこでも動くKotlinでの開発を楽しみましょう! :thumbsup:

おまけ dukatについて

Kotlin/JSにはTypeScriptの型をKotlin向けに変換してくれるdukatという仕組みがあり、Kotlin1.4.0からKotlin/JSに組み込まれています。
もちろん、TypeScriptをサポートしているGASにも用いることができ、うまく動けば型のサポートを得て補完などが効く快適なGAS開発環境を構築することができます。
この機能を利用するには、以下のようにbuild.gradleのdependenciesに追記します。

build.gradle
dependencies {
    ...
    implementation npm("@types/google-apps-script", "1.0.16", true)
}

@types/google-apps-scriptパッケージがGASのTypeScriptによる型定義を収めたもので、npmの第三引数にtrueを渡すことで、dukatによるKotlinコードの生成が有効になります。
これで最強のGAS開発環境ができた...!とは行かず、現状なぜか一部のコードの変換がうまく言っておらず、大量のunresolved referenceが発生してしまいビルドエラーになってしまうようでした :sweat_drops:

参考


  1. この画像で示されているコードは、変換前のコードが「決まった文字列を出力するだけのメソッド」なのでこれだけコンパクトになっています。とはいえ、以前のKotlinだと、たったそれだけのコードなのにKotlinのスタンダードライブラリのJS移植版がまるごとくっついてきてとても巨大になっていたので、相当に進化していることが伺えます。Kotlin側のコード規模に応じて変換後のコードも当然大きくなっていくのでご注意ください 

12
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
4