Kotlin/JS configuration made simple
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 whymoduleKind
needs to be specified incompileKotlin2Js
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 usingdevDependency
(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 inindex.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
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.