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

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

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


対象者

  • バックエンド/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 --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 


開発編

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:


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 を選びます



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 に出力されるようになります!

コメント

このブログの人気の投稿

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2020-12-01 09:41:49 RSSフィード2020-12-01 09:00 分まとめ(69件)