Help us understand the problem. What is going on with this article?

Kotlin/JSでマルチプラットフォーム対応のWeb開発を始めよう

2019/03/02更新

Kotlin/JSのビルドを @jetbrains/kotlin-webpack-plugin でビルドすると、 watchしていても毎回フルビルドが走る 仕様らしく規模が大きくなるにつれてビルド時間が掛かります。ログ上では Compiling Kotlin sources because the following files were changed: xxx.kt と表示が出るのでプラグイン側はきちんと差分ファイルを認識しているはずなのですが。
お試しなら大丈夫ですが本格開発する場合は 現状はGradleを使ったビルドをお勧めします (設定方法はいつかまとめたいと思います)。

対象者

  • バックエンド/AndroidとKotlinのコードを共有したいフロントエンド開発者
  • Android開発者でChrome ExtensionやPWAの開発をKotlinで始めてみたい方 (UIはマークアップできる人と組む想定)
  • 既存のフロントエンドプロジェクトにKotlinを導入してみたい開発者

などなど。サーバーサイドでNode.jsを使われる方も部分的/全面的に利用できると思います。

実現できること

  • Kotlinを使った堅牢なマルチプラットフォーム開発
    • サーバーサイドやネイティブアプリとモデルクラスやビジネスロジックを共有
      • JSに変換したKotlinのクラス・コードを他のJSからも呼べるように
  • webpackのwatchでKotlinのコードの更新に連動してビルド (ここ大事)
  • Dockerさえインストールされていたらすぐビルド可能
    • デザイナーなど非開発者でも簡単にプレビューできるようにしたい
    • Node.js / IntelliJ IDEAなどのインストール不要!Automatorとかでシェル操作も隠蔽したら完璧:star:

Kotlinの公式とか当たってみたけどJSとかと組み合わせて webpack --watch できる方法については こちらの記事 くらいしか見つからなかったのでまとめてみました。
この記事ではReactやVueといったライブラリを使った開発まではスコープに含めていません。

ソースコード

手っ取り早く動かしてみたい方はこちらからどうぞ。
https://github.com/cubenoy22/kotlin-js-webpack-sample

環境構築編

npmなどは説明しません。Dockerについて不明の場合は こちらの記事 を参照してください。
Webわかんないよーという方、Dockerのインストールを済ませつつリポジトリのコードを落として次の開発編にお進みください。 WebViewでちょっとJS連携したりできるレベルの知識があれば問題ありません。

(Web開発者はNode.jsインストールするのは当然と思っているけど、Docker化することで他分野のエンジニアでもすぐにフロントエンド開発に参加できるのはとてもありがたいですね:pray:)

プロジェクト構成

node_modules含めすべてのファイルはコンテナ側ではなくホスト側に持たせます(コンテナは使い捨て)。Dockerはいらないよという方、appの中だけで問題ありません。

  • your-project
    • app ... コンテナと共有するフォルダ
      • dist ... 成果物
        • MyProduct.js ... webpackでビルドされた最終JS
      • js
        • index.js ... エントリポイント (今回は MyProductKt.js をimportするだけ)
        • MyProductKt.js ... KotlinをビルドしてできたJSファイル
      • kt
        • main.kt ... ディレクトリで階層になっていてもOK
      • webpack.config.js
    • Dockerfile
    • docker-compose.yml

docker-compose.yml

上記のDocker記事 とまったく同じ内容のため省略します

Dockerfile

# 最新LTSの公式イメージ
FROM node:10.13-alpine
# webpackとKotlinに必要なパッケージのインストール
RUN apk --update add bash openjdk8
# カレントディレクトリ
WORKDIR /app
# nodeの代わりにbashを起動
ENTRYPOINT [ "bash" ]

npm init & install

一番最初だけalpine linuxイメージのダウンロードやbash/openjdk8のインストールが走るので少々時間がかかりますが、一度イメージが出来上がってしまえばすぐに起動してコンテナ内のbashに入れます。

$ docker-compose run --rm app
bash-4.4# npm init
bash-4.4# npm i -D webpack webpack-cli kotlin @jetbrains/kotlin-webpack-plugin

webpack.config.js

ミニマム設定を用意しました。これでも動きますがかなり端折ったので適宜追記してください。

const KotlinWebpackPlugin = require('@jetbrains/kotlin-webpack-plugin');

const BUILD_MODE = process.env.NODE_ENV || 'production';
// const IS_DEVEROPMENT = BUILD_MODE === 'development';

module.exports = {
    mode: BUILD_MODE,
    entry: {
        "MyProduct": __dirname + '/js/index.js'
    },
    plugins: [
        new KotlinWebpackPlugin({
            src: __dirname + '/kt',
            output: 'js',
            optimize: false,
            moduleName: 'MyProductKt', // ビルド時のJSファイル名
            moduleKind: 'commonjs',
            librariesAutoLookup: true,
            verbose: true,
        })
    ]
};

package.json

watchとbuildできるようにscriptsを登録しておきましょう。

{
  "前": "略",
  "scripts": {
    "test": "echo ...",
    "watch": "webpack -d --watch --watch-poll --progress --env.NODE_ENV=development",
    "build": "webpack --progress"
  },
  "後": "略"
}

環境構築完了

これでwebpackを使ってKotlinのコードをビルドできるようになりました :tada:
JSとは簡単にコラボレーションできますので必要に応じてBabelとかも入れてください。

開発再開、ビルドするためには

(Dockerのコンテナ立ち上げて) npm 呼ぶだけです。簡単。

$ docker-compose run --rm app
bash-4.4# npm install # 初回起動時は必須
bash-4.4# npm run watch # 開発する場合 or ↓
bash-4.4# npm run build # デプロイする場合

開発編

Kotlin/JVM では存在しない DOM 操作を体験するため、Qiita にちょっとした機能を付加する Chrome Extension を作ってみましょう:muscle:
快適な Developer Experience を体感するため、Kotlinファイルを編集する際は最後の方で紹介しているIntelliJ IDEAを使ってタイプしてみていただきたいです:bow:

bash-4.4# mkdir kt && touch kt/main.kt
bash-4.4# mkdir js && touch js/index.js

↑とりあえずディレクトリとファイルを作成します

js/index.js

いちおう既存Webプロジェクトへの追加を想定して、あえてindex.jsを作りました。importすればKotlinのクラスがJSから使えたりします。

import './MyProductKt';

kt/main.kt

importは補完を確定すると自動で追加されますので、import文とコメント行以外をコピペせずタイプしてみてください。npm run watchしながら書いて保存すると都度ビルドが走りますよ。

import org.w3c.dom.HTMLButtonElement
import org.w3c.dom.HTMLElement
import org.w3c.dom.HTMLTextAreaElement
import org.w3c.dom.asList
import kotlin.browser.document

fun copyToClipboard(text: String) {
    (document.createElement("textarea") as HTMLTextAreaElement).apply {
        value = text
        document.body!!.append(this)
        select()
        document.execCommand("copy")
        remove()
    }
}

fun main() {
    document.getElementsByClassName("code-frame").asList().forEach {
        val codeFrame = it as HTMLElement
        codeFrame.style.position = "relative"
        (document.createElement("button") as HTMLButtonElement).apply {
            val code = codeFrame.innerText
            innerText = "Copy"
            style.apply {
                right = "0"
                top = "0"
                position = "absolute"
                margin = "4px"
            }
            onclick = {
                copyToClipboard(code)
            }
            codeFrame.appendChild(this)
        }
    }
}

A.apply { method(); member = "" } というのは A.method(); A.member = "" と同じです。onclickのevent引数はここでは it で参照可能です。

ビルド

(環境構築編を飛ばされた方は開発編すぐ上にある「開発再開、ビルドするには」を参照してください)

bash-4.4# npm run build

distフォルダが出来上がりましたか?最後にChrome Extension用のマニフェストを書いておきます。

bash-4.4# touch dist/manifest.json

manifest.json

{
    "manifest_version": 2,
    "name": "QiitaCodeCopy",
    "version": "1.0.0",
    "description": "",
    "icons": {},
    "content_scripts": [{
        "matches": ["https://qiita.com/*/items/*"],
        "js": ["MyProduct.js"]
    }],
    "author": "YOUR_NAME_HERE"
}

さあ この辺り を参考にChrome Extensionをロードしてみてください。ディレクトリは dist を指定します。このページをリロードするとコードブロックの右上にCopyというボタンが現れましたね?おもむろにクリックしてエディタにペーストしてみると、ブロックの内容がコピーされているかと思います :tada:

(おまけ) IntelliJ IDEA の支援を受ける

いまのところ快適にKotlinがかけるIDEはJetBrains社製しかないと思うので、とりあえずIntelliJ IDEAで補完が効く状態まで持っていきたいと思います。

検証は IntelliJ IDEA 2018.2.7 (Ultimate Edition) と Kotlin プラグイン (1.3.10) で行いました。

プロジェクトの作成

Kotlin > Kotlin/JS を選びます

new_proj.png

Project name を自由に設定します、Project locationはktフォルダにしてください。Module nameは kt 、 Create source root はチェックを外して手動で行います。
(注: キャプチャは相対パスになっていますが実際は絶対パスにしておいてください)

proj_options.png

プロジェクトが出来上がりますが、source root がない状態なので指定します、Fileメニュー > Project Structure... を選びます。

module.png

Modules > kt を選んで Sources タブのルートにあるフォルダを選択し Mark as: Sources をクリックしてOKを押せばビルドが動くようになり /out に出力されるようになります!

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away