概要
kotlin-frontend-plugin は Kotlin/JS を用いてのフロントエンド開発を助ける Gradle プラグインです。 node.js を使うようになっており、webpack, karma などの node.js ライブラリを使った開発を助けます。 Kotlin の公式プラグインですが、現在は EAP となっています。
文書が少なく、何をどのように行っているのか分からなかったため、ソースコードを読みました。 本記事では、ソースコードから読み取った処理の要点を紹介します。 各々の関係を把握するには適していないため、別途図を作成する予定です。
目次
- 概要
- 目次
- 確認環境
- 解説
確認環境
- Kotlin 1.1.4
- kotlin-frontend-plugin 0.0.21
解説
class FrontendPlugin
org.jetbrains.kotlin.gradle.frontend.FrontendPlugin で行われている処理を書き記す。
project.extensions に kotlinFrontend (型は KotlinFrontendExtension) を追加する。
KotlinFrontendExtension で指定されている設定項目は以下のとおり。
- sourceMaps: Boolean = false
- downloadNodeJsVersion: String = “”
- nodeJsMirror: String = “”
project.tasks に以下のタスクを追加する。
- packages (group: build)
- bundle (group: build)
- run (group: run)
- stop (group: run)
- nodejs-download (型は NodeJsDownloadTask)
- kotlinFrontend.downloadNodeJsVersion が空でない場合のみ追加
- タスクの以下の項目を設定する
- version = kotlinFrontend.downloadNodeJsVersion
- mirror = kotlinFrontend.nodeJsMirror (nodeJsMirror の設定が存在する場合のみ)
タスクに依存関係を追加する。
ATask.dependsOn(BTask)
を BTask > ATask
として書くと以下になる。
nodejs-download > packages packages > compileKotlin2Js packages > compileTestKotlin2Js packages > bundle packages > run compileKotlin2Js > compileTestKotlin2Js compileKotlin2Js > bundle compileKotlin2Js > run bundle > assemble stop > clean
NpmPackageManager に対して以下を行う。
- apply(task packages)
- packages タスクを渡しているが使用されていない
- install(project)
- buildFinished & not failure & taskGraph is null の場合のみ実行される
kotlinFrontend extension で設定した *Bundle に対応する Bundler に対して以下を行う。 Bundler は webpack (WebPackBundler) か rollup (RollupBundler) のいずれか、もしくは両方。 * 部分は webpack か rollup を指定する。
- apply(project, NpmPackageManager, task bundle, task run, task stop)
Launcher に対して以下を行う。 Launcher は WebPackLauncher, KtorLauncher, KarmaLauncher の 3 つ。
- apply(NpmPackageManager, project, task run, task stop)
sourceSets.main.output に compileKotlin2Js.kotlinOptions.outputFile を登録する)。 但し、compileKotlin2Js.kotlinOptions.outputFile が存在するときのみ。
以降では、NpmPackageManager、Bundler から 1 つ (WebPackBundler)、Launcher から 1 つ (WebPackLauncher) 、 関連しているタスクについての調査結果を書く。 (全て調べるのは大変なので)
nodejs-download: NodeJsDownloadTask
~/.gradle/nodejs
ディレクトリに node.js をダウンロードする。
node 実行ファイルのパスを “$buildDir/nodePath.txt” に書き込む。
class NpmPackageManager
org.jetbrains.kotlin.gradle.frontend.npm.NpmPackageManager で行われている処理を書き記す。
apply
project.extensions に npm (型は NpmExtension) を追加する。
NpmExtension で指定されている設定項目は以下のとおり。
- dependencies: MutableList
- developmentDependencies: MutableList
- versionReplacements: MutableList
設定するためのメソッドは以下のとおり。
- dependency(name: String, version: String = “*”)
- devDependency(name: String, version: String = “*”)
- replaceVersion(name: String, version: String)
以下のいずれかの条件に該当する場合のみ、後に示す様々な処理を行う。
条件:
- npm.dependencies が空ではない
- npm.developmentDependencies が空ではない
- “$projectDir/package.json.d” ディレクトリが存在する
- requiredDependencies が空ではない
- require メソッドで設定される
処理:
compile.dependencies に NpmDependenciesTask.results のディレクトリを追加する。 (AbstractFileCollection として追加されており、具体的なディレクトリは遅延評価されると思われる。)
project.tasks に以下のタスクを追加する。
- npm-preunpack (型は UnpackGradleDependenciesTask)
- タスクの以下の項目を設定する
- dependenciesProvider = requiredDependencies (を返す関数)
- タスクの以下の項目を設定する
- npm-configure (group: NPM, 型は GeneratePackagesJsonTask)
- タスクの以下の項目を設定する
- dependenciesProvider = requiredDependencies (を返す関数)
- packageJsonFile = “$buildDir/package.json”
- npmrcFile = “$buildDir/.npmrc”
- タスクの以下の項目を設定する
- npm-install (group: NPM, 型は NpmInstallTask)
- タスクの以下の項目を設定する
- packageJsonFile = “$buildDir/package.json”
- タスクの以下の項目を設定する
- npm-index (型は NpmIndexTask)
- npm-deps (型は NpmDependenciesTask)
- npm (group: NPM)
タスクに依存関係を追加する。
nodejs-download > npm-configure npm-preunpack > npm-configure npm-configure > npm-install npm-install > npm-index npm-index > npm-deps npm-deps > npm npm > packages
install
以下のタスクのうち、state が EXECUTED, SKIPPED, UP-TO-DATE ではないタスクの execute() を呼ぶ。
- UnpackGradleDependenciesTask (= npm-preunpack)
- GeneratePackagesJsonTask (= npm-configure)
- NpmInstallTask (= npm-install)
- NpmIndexTask (= npm-index)
- NpmDependenciesTask (= npm-deps)
npm-* タスク
npm-preunpack: UnpackGradleDependenciesTask
npm.dependencies、npm.developmentDependencies、dependenciesProvider の返す Dependency、 いずれかの依存関係が存在するときのみ以下を行う。
compile configuration (build.gradle の compile 指定の依存設定) のうち、 Kotlin Java Script Library を “$buildDir/node_modules_imported” ディレクトリに展開する。 展開する際に package.json を生成する。
resultNames (型は MutableList
npm-configure: GeneratePackagesJsonTask
npm.dependencies、npm.developmentDependencies、dependenciesProvider の返す Dependency、 いずれかの依存関係が存在するときのみ以下を行う。
“$buildDir/package.json” ファイルを生成する。package.json の内容は以下のとおり。
- name
- moduleNames が 1 つならば、その名前
- moduleNames は compileKotlin2Js.kotlinOptions.outputFile のファイル名 (拡張子除く) のリスト
- 1 つでなければ、project.name
- いずれの名前も使えなければ、noname
- moduleNames が 1 つならば、その名前
- version
- project.version
- main
- moduleNames が 1 つならば、その名前
- 1 つでなければ、null
- dependencies
- 以下の 3 つを合わせた内容
- npm.dependencies
- resultNames (npm-preunpack タスクの結果) から生成された Dependency (RuntimeScope に設定)
- dependenciesProvider が生成した Dependency (RuntimeScope のみ)
- 以下の 3 つを合わせた内容
- devDependencies
- 以下の 2 つを合わせた内容
- npm.developmentDependencies
- dependenciesProvider が生成した Dependency (DevelopmentScope のみ)
- 以下の 2 つを合わせた内容
- 他
“$buildDir/.npmrc” ファイルを生成する。.npmrc の内容は以下のとおり。
progress=false # cache-min=3600
sourceSets.main.output.resourcesDir が設定されている場合には、 “${sourceSets.main.output.resourcesDir}/package.json” にも書き込む。
npm-install: NpmInstallTask
npm install
を実行する。
使用する npm コマンドは以下の順番で検索される。
- org.kotlin.frontend.node.dir プロパティで指定したパス
- “$buildDir/nodePath.txt” (NodeJsDownloadTask.nodePathTextFile) のパス
- 環境変数 PATH で指定したパス
npm-index: NpmIndexTask
“$buildDir/.modules.with.kotlin.txt” と “$buildDir/.modules.with.types.txt” を生成する。
“$buildDir/node_modules” のファイルを検査して、モジュールの絶対パスを .modules.with.*.txt ファイルに保存する。 kotlin と types それぞれで、以下に該当するファイルを探し、[module] の絶対パスを保存する。
- kotlin
- [module].jar
- [module]/META-INF/*.kotlin_module
- [module]/*.meta.js (但し、
// Kotlin.kotlin_module_metadata
で始まる)
- types
npm-deps: NpmDependenciesTask
“$buildDir/.modules.with.kotlin.txt” に記載されているディレクトリを results に読み込む。 results は NpmPackageManager の apply メソッドで使用される。
object WebPackBundler
apply(project, NpmPackageManager, task bundle, task run, task stop) で行われている処理を書き記す。
NpmPackageManager.require により以下のモジュール依存関係を追加する。
- webpack (version: *, scope: DevelopmentScope)
- webpack-dev-server (version: *, scope: DevelopmentScope)
- source-map-loader (version: *, scope: DevelopmentScope)
- kotlinFrontend.sourceMaps が true の場合のみ
project.tasks に以下のタスクを追加する。
- webpack-config (型は GenerateWebPackConfigTask)
- webpack-helper (型は GenerateWebpackHelperTask)
- webpack-bundle (groupd: webpack, 型は WebPackBundleTask)
タスクに依存関係を追加する。
webpack-config > webpack-helper webpack-config > webpack-bundle webpack-helper > webpack-bundle RelativizeSourceMapTask > webpack-bundle webpack-bundle > bundle
webpack-* タスク
webpack-config: GenerateWebPackConfigTask
kotlinFrontend extension で WebPackExtension が設定されており、 WebPackExtension.webpackConfigFile が設定されていない場合のみ以降の処理を行う。
以下に示す “$buildDir/webpack.config.js” を生成する。
<Part A|B|C>
は後述する内容が挿入される。
'use strict'; var webpack = require('webpack'); var config = <Part A>; var defined = <Part B>; config.plugins.push(new webpack.DefinePlugin(defined)); module.exports = config; <Part C>
Part A には以下の内容を持つ JSON が挿入される。
- context: compileKotlin2Js.kotlinOptions.outputFile の親ディレクトリ
- entry: 以下の内容を持つ JSON
- WebPackExtension.bundleName: “./${compileKotlin2Js.kotlinOptions.outputFile (拡張子を除く)}”
- output: 以下の内容を持つ JSON
- path: “$buildDir/bundle”
- filename: “[name].bundle.js”
- chunkFilename: “[id].bundle.js”
- publicPath: WebPackExtension.publicPath
- module: { rules: [ ] }
- resolve: { modules: <resolveRoots> }
- plugins: [ ]
Part B には kotlinFrontend extension の define(name: String, value: Any?) メソッドで設定された内容を持つ JSON が挿入される。
Part C には “$projectDir/webpack.config.d” 配下のファイルの内容が以下の形式で挿入される。 it はファイルを指す。 挿入順序はファイル名により決定される (拡張子前の数字で整列、数字がなければ 0 扱い)
// from file ${it.path} <ファイルの内容>
WebPackExtension の設定
kotlinFrontend extension には、bundle メソッドと allBundles メソッドが存在する。 いずれも BundleConfig を設定するメソッドである。
- bundle(id: String, configure: BundleConfig.() -> Unit)
- id: Bundler の種別 (webpack, rollup)
- id で指定した Bundler の設定を行う
- allBundles(block: BundleConfig.() -> Unit)
- 全ての Bundler の共通設定を行う
BundleConfig のインスタンスは WebPackBundler クラスで生成される WebPackExtension インスタンスである。 WebPackExtension クラスは BundleConfig インターフェースを実装している。
kotlinFrontend extension にて webpackBundle { ... }
を実行すると bundle("webpack") { ... }
が実行される。
WebPackExtension で設定できる項目は以下のとおり。
- bundleName: String = project.name
- sourceMapEnabled: Boolean = project.kotlinFrontend.sourceMaps
- contentPath: File? = null
- publicPath: String = “/”
- port: Int = 8088
- proxyUrl: String = “”
- stats: String = “errors-only”
- webpackConfigFile: Any? = null
webpack-helper: GenerateWebpackHelperTask
WebPackExtension.webpackConfigFile が設定されている場合のみ以降の処理を行う。
“$buildDir/WebPackHelper.js” を生成する。内容は以下のとおり。
module.exports = <JSON>
<JSON> には以下の内容を持つ JSON が挿入される。
- port: WebPackExtension.port
- shutDownPath: “/webpack/dev/server/shutdown” (WebPackRunTask.ShutDownPath)
- webPackConfig: WebPackExtension.webpackConfigFile
- contentPath: WebPackExtension.contentPath
- proxyUrl: WebPackExtension.proxyUrl (空ならば null に設定される)
- publicPath: WebPackExtension.publicPath
- sourceMap: kotlinFrontend.sourceMaps && WebPackExtension.sourceMapEnabled
- stats: WebPackExtension.stats
- bundlePath: compileKotlin2Js.kotlinOptions.outputFile
- moduleName: compileKotlin2Js.kotlinOptions.outputFile のファイル名 (拡張子を除く)
webpack-bundle: WebPackBundleTask
node $buildDir/node_modules/webpack/bin/webpack.js --config $buildDir/webpack.config.js
を実行する。
WebPackExtension.webpackConfigFile が設定されている場合は、"$buildDir/webpack.config.js" は設定したファイルが指定される。
object WebPackLauncher
apply(NpmPackageManager, project, task run, task stop) で行われている処理を書き記す。
project.tasks に以下のタスクを追加する。
- webpack-run (group: webpack, 型は WebPackRunTask)
- タスクの以下の項目を設定する
- start = true
- タスクの以下の項目を設定する
- webpack-stop (group: webpack, 型は WebPackRunTask)
- タスクの以下の項目を設定する
- start = false
- タスクの以下の項目を設定する
タスクに依存関係を追加する。
webpack-config > webpack-run RelativizeSourceMapTask > webpack-run webpack-run > run webpack-stop > stop
webpack-run: WebPackRunTask
node $buildDir/webpack-dev-server-run.js
を実行する。
実行する際に、起動するサーバーの情報を JSON 形式で “$buildDir/.run-webpack-dev-server.txt” に保存する。
“$buildDir/webpack-dev-server-run.js” はサーバー起動前に生成される。
kotlin-frontend-plugin 中の resource である kotlin/webpack/webpack-dev-server-launcher.js をテンプレートとして、
テンプレート中の require('$RunConfig$')
を GenerateWebpackHelperTask で生成している JSON に置き換える。
但し、JSON 中の内容において、WebPackExtension.webpackConfigFile が設定されていない場合は “$buildDir/webpack.config.js” が使用される。
サーバーの起動に成功すると “$buildDir/.run-webpack-dev-server.txt” が生成される。 以下の内容を持つ JSON ファイルである。
- port: WebPackExtension.port
- exts: kotlinFrontend extension の define メソッドで設定した内容
- hashes: 以下の内容を持つ JSON
webpack-stop: WebPackRunTask
http://localhost:$port/webpack/dev/server/shutdown
にリクエストを送信する。
$port
は “$buildDir/.run-webpack-dev-server.txt” が保持している port。