Kotlin/JS configuration made simple

Marcin Moskala
Kt. Academy
Published in
6 min readFeb 19, 2018

--

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
}
}
  1. This is how we say that we always want to use latest Node.js version.
  2. 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).
  3. 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.
  4. We configure webpack bundle. Bundle is the single JavaScript file that is created from all our Kotlin sources and dependencies.
  5. 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.
  6. We specify where are static files. They include index.html, images, css files etc.
  7. 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("...")
}
}
  1. 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

Kotlin/JS with kotlin-frontend-plugin is fully functional and ready to be used. It is simple and intuitive. This plugin is important especially in multiplatform projects where we need to use Gradle to build frontend modules. Migration in Kt. Academy application was simple and it mainly included deletion of obsolete configuration files. Also, the fact that this plugin was created by Kotlin team makes it safe to use. It looks like final Kotlin frontend configuration. I think it is a big step for Kotlin, as a multiplatform solution.

Learned something? Click the 👏 to say “thanks!” and help others find this article.

If you think that this is important, share it with others.

Do you need a Kotlin workshop? Visit our website to see what we can do for you.

To be up-to-date with great news on Kt. Academy, subscribe to the newsletter, observe Twitter and follow us on medium.

I would like to thank Edvin Syse for his important contribution to example project. Thanks also to Ilya Gorbunov for important correction to this article.

--

--

Kt. Academy creator, co-author of Android Development with Kotlin, author of open-source libraries, community activist. http://marcinmoskala.com/