If you did Kotlin/JS with Gradle before, the configuration was probably quite complex. Webpack and Gradle calling each other, multiple Webpack configuration files… it was a mess.

Fortunately, now it has changed and you can use new possibilities to make your configuration clear and simple. I am happy to explain how you can make frontend Gradle configuration using kotlin-frontend-plugin . Note that this is an official Kotlin plugin so it is here to stay and that it is going to be developed together with rest of the Kotlin ecosystem.

Basic configuration

In minimal version, all you need in your Gradle configuration is this:

apply plugin: 'org.jetbrains.kotlin.frontend'

apply plugin: 'kotlin2js' // kotlin2js configuration

compileKotlin2Js {

kotlinOptions

metaInfo = true

outputFile = "$project.buildDir.path/js/${project.name}.js"

sourceMap = true

moduleKind = 'commonjs'

main = "call"

}

} dependencies {

compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version"

// Your other dependencies

}

Using this configuration, you can build a fully functional project. If you analyze it, you can find dependencies and kotlin2js configuration that says how Kotlin files should be compiled. There is no kotlin-frontend-plugin ( org.jetbrains.kotlin.frontend ) configuration. It is because the plugin can work with default one. Although we can specify one, using kotlinFrontend block. Inside we can specify some important things like:

Node.js version we want to use

npm dependencies

configuration for webpack

Let’s see how we can do it.

Webpack currently requires moduleKind to be set to one of the following: 'commonjs' , 'amd' or 'umd' . 'plain' , that is default, is not suitable. This is why moduleKind needs to be specified in compileKotlin2Js block if we want to use the plugin.

Kotlin Frontend plugin configuration

Let’s analyze configuration from Kot. Academy application as an example:

kotlinFrontend {

downloadNodeJsVersion = 'latest' // 1 npm { // 2

dependency("firebase", "^4.6.2")

dependency("react", "15.6.1")

dependency("react-dom", "15.6.1")

dependency("react-router-dom", "4.2.2")

} sourceMaps = true // 3 webpackBundle { // 4

bundleName = "main" // 5

contentPath = file('src/main/web') // 6

proxyUrl = "http://localhost:8080" // 7

}

}

This is how we say that we always want to use latest Node.js version. We specify npm dependencies. We can either require normal dependencies using dependency , or development dependencies using devDependency (these dependencies won’t be distributed with your application). We enable source maps. Source maps help us when we need to debug application — they allow us to operate on Kotlin code instead of on obscured JavaScript result that is created by kotlin2js plugin. We configure webpack bundle. Bundle is the single JavaScript file that is created from all our Kotlin sources and dependencies. We can specify bundle name. When we set it to “XXX” then generated bundle will be named “XXX.bundle.js”. The default name is the name of a module, so in this example, it would be “web.bundle.js”. Once we set name to “main”, the generated bundle is “main.bundle.js”. Important information: This naming differentiates this plugin from the previous configuration. Once you’ve moved to kotlin-frontend-plugin , remember to change bundle name in index.html . We specify where are static files. They include index.html , images, css files etc. Here we specify where is our backend web-server located. All external calls will be directed there.

The presented configuration specifies both Gradle and Webpack dependencies, and they are both downloaded when we build a project. The plugin lets us use two build systems together and use their best strengths during a single build.

That’s it - for a fully functional project! Gradle and Webpack configured so easily in a single file. That’s so simple :D

If you need something more, there are some other webpack configurations:

kotlinFrontend {

webpackBundle {

bundleName = "main"

sourceMapEnabled = true|false // enable/disable source maps

contentPath = file(...) // a file that represents a directory to be served by dev server)

publicPath = "/" // web prefix

host = "localhost" // dev server host

port = 8088 // dev server port

proxyUrl = "" | "http://...." // URL to be proxied, useful to proxy backend webserver

stats = "errors-only" // log level

}

}

Building and running

We can run frontend locally using run task: (in all this commands we suppose that frontend module is named web )

./gradlew :web:run

This starts background daemon that serves website.

Once started website will be running in the background. We can stop it using stop task:

./gradlew :web:stop

We can just build a module using build task:

./gradlew :web:build

If we need only bundle generated, we can use bundle task:

./gradlew :web:bundle

Note that if we need to distribute an application to some external server, we need to build bundle and copy it together with the static files info backend folder that is statically served. You can do it using copy task. Here we define serverPrepare task that is used to prepare jar that is ready to serve both backend and frontend.

Hot replacement

The plugin supports hot replacement. In the simplest case, all you need is to run a module with -t flag:

./gradlew -t :web:run

If we need to keep some data during hot replacement, then we need to keep them in the state and restore them after reload. This is how it can be done:

module.hot?.let { hot ->

hot.accept() // accept hot reload

hot.dispose { data ->

data.my-fields = myGetStateFunction(data)

}

} module.hot?.data?.let { data ->

myRestoreFunction(data)

}

Testing

The plugin supports testing using Karma framework, but it needs additional configuration. Here is an example:

kotlinFrontend {

karma {

port = 9876

runnerPort = 9100

reporters = listOf("progress")

frameworks = listOf("qunit") // 1

preprocessors = listOf("...")

}

}

Currently only Qunit is supported.

Instead, we can also use classic Karma configuration:

kotlinFrontend {

karma {

customConfigFile = "myKarma.conf.js"

}

}

We can run tests using test task:

./gradlew :web:test

Summary