Kotlin JS + React, unstable building

Hi!
I’m building a project using kotlin/JS (org.jetbrains.kotlin.js) and React and while I can get it to work, most of the time I get errors that disappear when I simply run it again. These errors include:

<PATH TO PROJECT>/build/js/packages/<PROJECT NAME>/package.json (No such file or directory)

Cannot find node module "webpack-dev-server/bin/webpack-dev-server.js" in "<PATH TO PROJECT>/build/js/packages/<PROJECT NAME>"

Cannot find node module "webpack/bin/webpack.js" in "<PATH TO PROJECT>/build/js/packages/<PROJECT NAME>"

and sometimes others (cannot follow symlink or something), although these two are the most common.
The most annoying part is that these errors also break the hot-reload of browserRun or browserRun --continuous, having me to restart the run.

My current build.gradle.kts:

import org.gradle.kotlin.dsl.*

plugins {
    kotlin("js") version "1.3.61"
    kotlin("kapt") version "1.3.61"
    id("com.diffplug.gradle.spotless") version "3.26.1"
}

apply {
    //    plugin("kotlin-dce-js")
}

group = "org.example"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
    jcenter()
    maven(url = "https://dl.bintray.com/kotlin/kotlinx")
    maven(url = "https://dl.bintray.com/kotlin/kotlin-eap")
    maven(url = "https://kotlin.bintray.com/js-externals")
    maven(url = "https://kotlin.bintray.com/kotlin-js-wrappers")
}

// attempt at linter
spotless {
    kotlin {
        ktlint()
    }
    kotlinGradle {
        // same as kotlin, but for .gradle.kts files (defaults to '*.gradle.kts')
        target("*.gradle.kts", "additionalScripts/*.gradle.kts")
        ktlint()
    }
}


kotlin {
    target {
        useCommonJs()
        nodejs()
        browser {
            compilations.all {
                kotlinOptions {
                    metaInfo = true
                    outputFile = "${project.buildDir.path}/js/${project.name}.js"
                    sourceMap = true
                    sourceMapEmbedSources = "always"
                    moduleKind = "commonjs"
                    main = "call"
                }
            }
        }
    }


    sourceSets {
        main {
            dependencies {
                implementation("org.jetbrains:annotations:16.0.2")
                implementation(kotlin("stdlib-js"))
                implementation("org.jetbrains.kotlinx:kotlinx-html-js:0.6.12")
                implementation("org.jetbrains:kotlin-react:16.9.0-pre.89-kotlin-1.3.60")
                implementation("org.jetbrains:kotlin-react-dom:16.9.0-pre.89-kotlin-1.3.60")
                implementation("io.data2viz:d2v-data2viz-js:0.8.0-RC1")
                implementation("org.jetbrains:kotlin-extensions:1.0.1-pre.89-kotlin-1.3.60")
                implementation("org.jetbrains:kotlin-css:1.0.0-pre.89-kotlin-1.3.60")
                implementation("org.jetbrains:kotlin-css-js:1.0.0-pre.89-kotlin-1.3.60")
                implementation("org.jetbrains:kotlin-styled:1.0.0-pre.89-kotlin-1.3.60")
                implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:0.11.1")

                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.1")
                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:1.3.1")

                implementation(npm("react", "16.12.0"))
                implementation(npm("react-dom", "16.12.0"))
                implementation(npm("react-draggable"))
                implementation(npm("react-list"))
                implementation(npm("react-window"))

                implementation(npm("inline-style-prefixer"))
                implementation(npm("core-js"))
                implementation(npm("@material-ui/core"))
                implementation(npm("@material-ui/icons"))

                implementation(npm("styled-components"))
                implementation(npm("jquery"))
            }
        }
    }
}

@Jolanrensen Hi! I reproduced your problem. I changed outputFile property with outputFile = "${project.path}/js/${project.name}.js" and it helped me. If it will not help you, can you attach a sample project?

1 Like

Thanks for your reply!
In my case changing the output file did not work (Webpack couldn’t find any of the npm dependencies anymore).
However, it did work when I commented out the outputFile entirely!
It’s really fast now, however, the hot reload does not work yet, I get

[WDS] Errors while compiling. Reload prevented.

Do you perhaps know of a setting to force refresh Webpack to reload after a rebuild?

Btw, this is the error, basically it cannot find the project’s .js file, which makes sense as it’s rebuilding:

./kotlin/<PROJECT NAME>.js
Module build failed (from <PATH TO PROJECT>/build/js/packages_imported/kotlin-source-map-loader/1.3.61/kotlin-source-map-loader.js):
Error: ENOENT: no such file or directory, open '<PATH TO PROJECT>/build/js/packages/<PROJECT NAME>/kotlin/<PROJECT NAME>'

I think I fixed it!
I found Kotlin JS incremental compilation, but when I tried to copy the solution in a webpack.config.d/webpack.config.js in the project root, the server wouldn’t start at all (I assume because the devServer settings would be overwritten). So I copied the config.devServer settings from the auto-generated webpack.config.js, added the watchOptions, like this:

config.devServer = {
    "inline": true,
    "lazy": false,
    "noInfo": true,
    "open": true,
    "overlay": false,
    "port": 8080,
    "watchOptions": {
        "aggregateTimeout": 5000,
        "poll": 1000
    },
    "contentBase": [
        "<PATH TO PROJECT>/build/processedResources/Js/main"
    ]
};

and now hot reloading works!
Let me say that this is quite a hacky solution, so if anyone knows how to add the watchOptions part using gradle, let me know!

Hi, it is almost right solution, but if you write

config.devServer = {
    watchOptions: {
        aggregateTimeout: 5000,
        poll: 1000
    }
}

You overwrite previous settings for devServer.
Instead of this, you can add some additional properties to your devServer if you want (like watchOptions in your case)
You can do it in webpack.config.d/<any>.js:

config.devServer = config.devServer || {} // create devServer in case it is undefined
config.devServer.watchOptions = {
    "aggregateTimeout": 5000,
    "poll": 1000
}
2 Likes

Hello! Is there any progress on the issue? I have the same problem with Kotlin version 1.3.61.

The proposed workaround is working but it’s very slow. The Kotlin compiler can rebuild my project in less than a second but I have to wait another 5 seconds because of the “aggregateTimeout”. If you changed only the color of a button, it’s very annoying. Also, it is not a robust solution. If compilation last longer than 5 seconds, the error will still happen.

I was able to fix it by redirecting the Kotlin compiler output to another file, then copying that file back to the webpack entry. It’s still a workaround, but it works better.

How to use my fix:

  1. Add copy file action in compileKotlinJs.doLast block.
compileKotlinJs {
    doLast {
        copy {
            from "$buildDir/js/packages/${project.name}/kotlin"
            into "$buildDir/js/entry/kotlin"
        }
    }
}
  1. Add file to webpack.config.d that will change the webpack entry.
// devServer is an indicator of development build.
if (config.devServer) {
    const projectName = config.output.filename.replace('.js', '');
    config.entry = config.entry.map(
        s => s.replace(`/js/packages/${projectName}/kotlin/`, "/js/entry/kotlin/"),
    );
}

This workaround works with 1.3.61 version but breaks in 1.3.70.