2020/03/29更新
公式からGradleを使う方法を強く推奨するという注意書きが足されました。Kotlin/JSは既存のフロントエンドに溶け込むのではなく、Kotlin/JSのプロジェクトにフロントエンドの資産を活用していくスタイルが推奨のようです。
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とかでシェル操作も隠蔽したら完璧
Kotlinの公式とか当たってみたけどJSとかと組み合わせて webpack --watch
できる方法については こちらの記事 くらいしか見つからなかったのでまとめてみました。
この記事ではReactやVueといったライブラリを使った開発まではスコープに含めていません。
ソースコード
手っ取り早く動かしてみたい方はこちらからどうぞ。
https://github.com/cubenoy22/kotlin-js-webpack-sample
環境構築編
npmなどは説明しません。Dockerについて不明の場合は こちらの記事 を参照してください。
Webわかんないよーという方、Dockerのインストールを済ませつつリポジトリのコードを落として次の開発編にお進みください。
WebViewでちょっとJS連携したりできるレベルの知識があれば問題ありません。
(Web開発者はNode.jsインストールするのは当然と思っているけど、Docker化することで他分野のエンジニアでもすぐにフロントエンド開発に参加できるのはとてもありがたいですね)
プロジェクト構成
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
- dist ... 成果物
- Dockerfile
- docker-compose.yml
- app ... コンテナと共有するフォルダ
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のコードをビルドできるようになりました
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 を作ってみましょう
快適な Developer Experience を体感するため、Kotlinファイルを編集する際は最後の方で紹介しているIntelliJ IDEAを使ってタイプしてみていただきたいです
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というボタンが現れましたね?おもむろにクリックしてエディタにペーストしてみると、ブロックの内容がコピーされているかと思います
(おまけ) IntelliJ IDEA の支援を受ける
いまのところ快適にKotlinがかけるIDEはJetBrains社製しかないと思うので、とりあえずIntelliJ IDEAで補完が効く状態まで持っていきたいと思います。
検証は IntelliJ IDEA 2018.2.7 (Ultimate Edition) と Kotlin プラグイン (1.3.10) で行いました。
プロジェクトの作成
Kotlin > Kotlin/JS を選びます
Project name を自由に設定します、Project locationはktフォルダにしてください。Module nameは kt
、 Create source root はチェックを外して手動で行います。
(注: キャプチャは相対パスになっていますが実際は絶対パスにしておいてください)
プロジェクトが出来上がりますが、source root がない状態なので指定します、Fileメニュー > Project Structure... を選びます。
Modules > kt を選んで Sources タブのルートにあるフォルダを選択し Mark as: Sources
をクリックしてOKを押せばビルドが動くようになり /out に出力されるようになります!