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

今年も残り1ヶ月となったのでKotlin/JSでChrome Extension作るよ!!!
来年は盛り上がると嬉しいな!

この記事ではReactやVueといったライブラリを使った開発まではスコープに含めていません。
手っ取り早く動かしてみたい方はこちらからどうぞ。
https://github.com/cubenoy22/kotlin-js-webpack-sample
npmなどは説明しません。Dockerについて不明の場合は こちらの記事 を参照してください。
(Web開発者はNode.jsインストールするのは当然と思っているけど、Docker化することで他分野のエンジニアでもすぐにフロントエンド開発に参加できるのはとてもありがたいですね
)
node_modules含めすべてのファイルはコンテナ側ではなくホスト側に持たせます(コンテナは使い捨て)。Dockerはいらないよという方、appの中だけで問題ありません。
上記のDocker記事 とまったく同じ内容のため省略します
一番最初だけalpine linuxイメージのダウンロードやbash/openjdk8のインストールが走るので少々時間がかかりますが、一度イメージが出来上がってしまえばすぐに起動してコンテナ内のbashに入れます。
ミニマム設定を用意しました。これでも動きますがかなり端折ったので適宜追記してください。
watchとbuildできるようにscriptsを登録しておきましょう。
これでwebpackを使ってKotlinのコードをビルドできるようになりました 
JSとは簡単にコラボレーションできますので必要に応じてBabelとかも入れてください。
(Dockerのコンテナ立ち上げて)
Kotlin/JVM では存在しない DOM 操作を体験するため、Qiita にちょっとした機能を付加する Chrome Extension を作ってみましょう
快適な Developer Experience を体感するため、Kotlinファイルを編集する際は最後の方で紹介しているIntelliJ IDEAを使ってタイプしてみていただきたいです
↑とりあえずディレクトリとファイルを作成します
いちおう既存Webプロジェクトへの追加を想定して、あえてindex.jsを作りました。importすればKotlinのクラスがJSから使えたりします。
importは補完を確定すると自動で追加されますので、import文とコメント行以外をコピペせずタイプしてみてください。npm run watchしながら書いて保存すると都度ビルドが走りますよ。
distフォルダが出来上がりましたか?最後にChrome Extension用のマニフェストを書いておきます。
さあ この辺り を参考にChrome Extensionをロードしてみてください。ディレクトリは 
今回はwebpackを使ったwatchビルドを意識したため、Androidのnpm的存在であるGradleを使用していません。まだJavaの膨大な資産がどこまでWebに使えるのかリサーチできていないため近々調べられたらなと思います。
また出来上がったJSがとても大きくGradleなら JavaScript DCE を使って無駄なKotlinのライブラリのコードを削減できるのですが、webpackだけの場合はやり方がわかりませんでした。こちらも調査したいです。
いまのところ快適に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は
(注: キャプチャは相対パスになっていますが実際は絶対パスにしておいてください)
プロジェクトが出来上がりますが、source root がない状態なので指定します、Fileメニュー > Project Structure... を選びます。
Modules > kt を選んで Sources タブのルートにあるフォルダを選択し Mark as:
対象者
- バックエンド/AndroidとKotlinのコードを共有したいフロントエンド開発者
-
Android開発者でChrome ExtensionやPWAの開発をKotlinで始めてみたい方 (UIはマークアップできる人と組む想定) - 既存のフロントエンドプロジェクトにKotlinを導入してみたい開発者
実現できること
- Kotlinを使った堅牢なマルチプラットフォーム開発
- サーバーサイドやネイティブアプリとモデルクラスやビジネスロジックを共有
- JSに変換したKotlinのクラス・コードを他のJSからも呼べるように
- サーバーサイドやネイティブアプリとモデルクラスやビジネスロジックを共有
- webpackのwatchでKotlinのコードの更新に連動してビルド (ここ大事)
-
Dockerさえインストールされていたらすぐビルド可能
- デザイナーなど非開発者でも簡単にプレビューできるようにしたい
- Node.js / IntelliJ IDEAなどのインストール不要!Automatorとかでシェル操作も隠蔽したら完璧

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 --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
開発編
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
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"
}
dist を指定します。このページをリロードするとコードブロックの右上にCopyというボタンが現れましたね?おもむろにクリックしてエディタにペーストしてみると、ブロックの内容がコピーされているかと思います
Next Step
今回はwebpackを使ったwatchビルドを意識したため、Androidのnpm的存在であるGradleを使用していません。まだJavaの膨大な資産がどこまでWebに使えるのかリサーチできていないため近々調べられたらなと思います。また出来上がったJSがとても大きくGradleなら JavaScript DCE を使って無駄なKotlinのライブラリのコードを削減できるのですが、webpackだけの場合はやり方がわかりませんでした。こちらも調査したいです。
(おまけ) 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 に出力されるようになります!
コメント
コメントを投稿