A new dead code elimination tool for JS

A new 1.1.4 EAP provides a new cool feature for JavaScript: a dead code elimination (DCE) tool. This tool allows to strip out unused pieces of code from generated JS. When you are writing an application, you, hopefully, don’t write unused code. However, you might use libraries, which may be huge and of which you use only small parts. Examples are: kotlin.js (~1.3 mb), kotlinx-html-js.js (~550 kb). DCE tool is good at removing unused code from these libraries. Currently, DCE reduces size of kotlin.js file down to ~65 kb for a simple “Hello, World” application.

How to use

DCE tool is currently available from Gradle and as a command-line tool. We will describe how to use it from CLI later. For now, we are going to show how to use it from Gradle.

Simply add the following line to your build.gradle:

apply plugin: 'kotlin-dce-js'

Note that if you are using multi-project build, you should apply plugin to the main project that is an entry point to your application.

For example as for 1.1.4 EAP, the build script may look like this:

group 'org.jetbrains.kotlin'
version '1.0-SNAPSHOT'

buildscript {
    ext.kotlin_version = '1.1.4-eap-11'

    repositories {
        maven { url 'https://dl.bintray.com/kotlin/kotlin-eap-1.1' }
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

apply plugin: 'kotlin2js'
apply plugin: 'kotlin-dce-js'

repositories {
    maven { url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1' }
    mavenCentral()
}

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

By default, the resulting set of JavaScript files (your application together with all dependencies) can be found at build/classes/main/min/.

Configuring

To configure DCE on the main source set, you can use the runDceKotlinJs task (and corresponding runDce<sourceSetName>KotlinJs for other source sets).

Sometimes you are going to use a Kotlin declaration directly from JavaScript, and it’s being stripped out by DCE. You may want to keep this declaration. To do so, you can use the following syntax in build.gradle:

runDceKotlinJs.keep "declarationToKeep"[, "declarationToKeep", ...]

Where declarationToKeep has the following syntax:

moduleName.dot.separated.package.name.declarationName

For example, if our module is named kotlin-js-example and we have a function named toKeep in package org.jetbrains.kotlin.examples, we should use the following line:

runDceKotlinJs.keep "kotlin-js-example_main.org.jetbrains.kotlin.examples.toKeep"

Note that if your function has parameters, its name will be mangled, so the mangled name should be used in the keep directive.

Notes

  • DCE tool is an experimental feature. This does not mean we are going to remove it, or that it’s unusable for production. This means that we can change names of configuration parameters, default settings, etc.
  • Currently you should not use DCE tool if your project is a shared library. It’s only applicable when you are developing an application (which may use shared libraries). The reason is: DCE does not know which parts of the library are going to be used by the user’s application.
  • DCE does not perform minification (uglification) of your code by removing unnecessary whitespaces and shortening identifiers. You should use existing tools, like UglifyJS (GitHub - mishoo/UglifyJS: JavaScript parser / mangler / compressor / beautifier toolkit) or Google Closure Compiler (Closure Compiler  |  Google Developers) for this purpose.
  • Full documentation and examples will be available later.
9 Likes

Looks like this tool works by processing compileKotlin2Js output + kotlin.js into “min” sub folder which is a nice bonus, no need to copy kotlin.js manually anymore.

If anybody tried using it as cli tool I would recommend using plugin instead, it is plain simpler, and works ok, no need to build kotlin sdk etc.

Also it is really efficient (or kotlin.js stdlib really not efficient :slight_smile: ) With 60kloc kotlin (js target part) project it reduces kotlin.js from 1.2MB down to 247kb. No bugs encountered everything is still working.

It is hard to understand how kotlin gradle plugin is working btw, I was not able to deduce any options if any for runDceKotlinJs task by looking at code.

Man, this plugin is really nice! Like said before there is no need to copy the dependency files. You just need to configure a bundler like webpack to create a bundle using the min directory.

Nice work, guys!

BTW, here’s an example of using DCE with webpack: https://github.com/JetBrains/kotlin-examples/tree/master/gradle/js-dce

I’ll give a link to this example on kotlinlang.org as soon as we release 1.1.4

Nice!

I’m using this, but I realized that apply plugin: 'kotlin-dce-js' is all that’s needed for the plugin to run.

No need to add runDceKotlinJs to any configuration.

My problem is now I’ld like to turn off dce for the test-sources, is that possible?

Is it possible to generate source maps with kotlin-dce-js plugin?

Currently, DCE does not support source maps, but it’s planned to support them soon, see KT-19821

1 Like

Is there any example using kotlin gradle script and the plugins DSL?

After running this plugin on a client react app which depends on a library coming from an multiplatform project which uses ktor & kotlinx-serialization I got the following error:

kotlinx-serialization-runtime-js.js:9722 Uncaught TypeError: e.defineModule is not a function
at Object.r (kotlinx-serialization-runtime-js.js:9722)
at Object.<anonymous> (kotlinx-serialization-runtime-js.js:3)
at n (bootstrap:19)
at Object.<anonymous> (mu51k-core.js:3504)
at n (bootstrap:19)
at Object.<anonymous> (AuthResponseUtils.kt:8)
at Object.<anonymous> (AuthResponseUtils.kt:8)
at n (bootstrap:19)
at Object.<anonymous> (hyphenateProperty.js:17)
at n (bootstrap:19)

The error is at this line: Kotlin.defineModule('kotlinx-serialization-runtime-js', _);

How could “Kotlin” not be available in bundle.js?
Should I “keep defineModule”? If so, how do runDceKotlinJs.keep translates to Kotlin Gradle DSL?

ps: This is through webpack. In development it works perfectly (but bundle.js is like 25MB so I am trying to reduce that to much less).

Thank you in advance,
Alex