jaro2gw
jaro2gw

Reputation: 133

Webpack not serving Kotlin Multiplatform React-JS App

I'm trying to configure a template for Kotlin Multiplatform project running ktor on backend and react on frontend with Kotlin type-safe wrapper from the jetbrains team. To share code between frontend and backend I need to use gradle

build.gradle.kts:

val kotlin_version: String by project

val ktor_version: String by project
val logback_version: String by project

val annotations_version: String by project

val kotlin_react_version: String by project
val kotlin_react_dom_version: String by project
val kotlin_extensions_version: String by project
val kotlin_css_version: String by project
val kotlin_css_js_version: String by project
val kotlin_styled_version: String by project

val kotlinx_serialization_version: String by project
val kotlinx_html_version: String by project

plugins {
    kotlin("multiplatform") version "1.3.61"
    kotlin("kapt") version "1.3.61"
    kotlin("plugin.serialization") version "1.3.61"
}

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

group = "com.jaro2gw"
version = "0.0.1"

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


kotlin {
    js("frontend") {
        useCommonJs()
        nodejs()
        browser {
            compilations.all {
                kotlinOptions {
                    metaInfo = true
                    sourceMap = true
                    sourceMapEmbedSources = "always"
                    moduleKind = "commonjs"
                    main = "call"
                }
            }
        }
    }

    jvm("backend")

    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation(kotlin("stdlib-common"))
            }
        }

        val frontendMain by getting {
            dependsOn(commonMain)
            dependencies {
                implementation(kotlin("stdlib-js", kotlin_version))

                implementation(kotlinDependency("react", kotlin_react_version))
                implementation(kotlinDependency("react-dom", kotlin_react_dom_version))
                implementation(kotlinDependency("extensions", kotlin_extensions_version))
                implementation(kotlinDependency("css", kotlin_css_version))
                implementation(kotlinDependency("css-js", kotlin_css_js_version))
                implementation(kotlinDependency("styled", kotlin_styled_version))

                implementation(kotlinxDependency("html-js", kotlinx_html_version))
                implementation(kotlinxDependency("serialization-runtime-js", kotlinx_serialization_version))

                implementation("org.jetbrains:annotations:$annotations_version")

                implementation(npm("webpack"))
                implementation(npm("webpack-cli"))
                implementation(npm("webpack-dev-server"))

                implementation(npm("react"))
                implementation(npm("react-dom"))
                implementation(npm("react-draggable"))
                implementation(npm("react-list"))
                implementation(npm("react-is"))

                implementation(npm("inline-style-prefixer"))
                implementation(npm("core-js"))
                implementation(npm("styled-components"))
                implementation(npm("jquery"))
            }

        }

        val backendMain by getting {
            dependsOn(commonMain)
            dependencies {
                implementation(kotlin("stdlib-jdk8", kotlin_version))
                implementation(ktorDependency("server-netty"))
                implementation(ktorDependency("server-core"))
                implementation(ktorDependency("locations"))
                implementation(ktorDependency("server-sessions"))
                implementation(ktorDependency("websockets"))
                implementation(ktorDependency("gson"))
                implementation("ch.qos.logback:logback-classic:$logback_version")
            }
        }
    }
}

fun ktorDependency(name: String, version: String = ktor_version) = "io.ktor:ktor-$name:$version"
fun kotlinDependency(name: String, version: String) = "org.jetbrains:kotlin-$name:$version"
fun kotlinxDependency(name: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$name:$version"

gradle.properties:

kotlin.code.style=official

kotlin_version=1.3.61

ktor_version=1.3.0
logback_version=1.2.1

kotlin_react_version=16.9.0-pre.90-kotlin-1.3.61
kotlin_react_dom_version=16.9.0-pre.90-kotlin-1.3.61
kotlin_extensions_version=1.0.1-pre.90-kotlin-1.3.61
kotlin_css_version=1.0.0-pre.90-kotlin-1.3.61
kotlin_css_js_version=1.0.0-pre.90-kotlin-1.3.61
kotlin_styled_version=1.0.0-pre.90-kotlin-1.3.61

annotations_version=16.0.2

kotlinx_serialization_version=0.11.1
kotlinx_html_version=0.6.12

Project structure looks like this:

/src    
    /backendMain (ktor kotlin code)
    /commonMain
        /kotlin
            /model
                >User.kt
    /frontendMain
        /kotlin
            >index.kt
        /resources
            /public
                >index.html
/webpack.config.d
    >webpack.config.js

User.kt

package model

data class User(val ID: Long, val name: String)

index.kt

import kotlinext.js.require
import kotlinext.js.requireAll
import react.dom.render
import kotlin.browser.document
import model.User

fun main() {
    requireAll(require.context("kotlin", true, js("/\\.css$/")))

    val user = User(1, "ReactUser")
    render(document.getElementById("root")) {
        +"Hello, $user!"
    }
}

index.html

<!doctype html>
<html lang="en">
<head>
<!-- some meta information, shortcut icon and title -->
</head>
<body>
<noscript>
    You need to enable JavaScript to run this app.
</noscript>
<div>Hello</div>
<div id="root"></div>
</body>
</html>


webpack.config.js:

path = require("path")

config = config || {};
config.devServer = config.devServer || {};
config.devServer = {
    "hot": true,
    "open": false,
    "port": 3000,
    contentBase: [
        path.resolve(__dirname, "..\\..\\..\\processedResources\\frontend\\main\\public")
    ]
}
config.devServer.proxy = config.devServer.proxy || {};
config.devServer.proxy = {
    "/api": "https://localhost:8080"
}
config.devServer.watchOptions = config.devServer.watchOptions || {};
config.devServer.watchOptions = {
    "aggregateTimeout": 5000,
    "poll": 1000
};

module.exports = {
    entry: {
        main: path.resolve(__dirname, "kotlin\\kotlin-fullstack-mpp-frontend.js")
    }
}

Backend runs smoothly, the frontend, however, fails to load the generated .js file (I think?).

The expected html page when accessing localhost:3000 should look somewhat like this:

Hello

Hello, (ID=1, name="ReactUser")!

but what I get is just the first Hello

I think that problem is with the auto-generated webpack.config.js . I searched the internet for a bit and found out you can include your own webpack.config.js file in the project. I managed to set the contentBase, proxy and other options, but seem unable to correctly configure entry.main option? I think that the project structure might be also playing role here. The build folder structure is as follows:

/build
    /js
        /node_modules (project dependencies)
        /packages
            /kotlin-fullstack-mpp-frontend
                /kotlin
                    /kotlin-fullstack-mpp-frontend (compiled common code)
                    >kotlin-fullstack-mpp-frontend.js (compiled frontend code)
                /node-modules (not sure what is here)
                >package.json
                >webpack.config.js
        /packages_imported (kotlin wrapper for react)
        >package.json
        >yarn.lock
    /processedResources
        /frontend
            /main
                /public
                    >index.html
    /reports
        /webpack
            /kotlin-fullstack-mpp-frontend
                >webpack.config.evaluated.js

To build and run the react app I use gradlew frontendBrowserRun -t

Whole project is accessible here: https://github.com/jaro2gw/kotlin-fullstack-mpp

[EDIT]

The issue is obsolete because Intellij IDEA now contains a project wizard for kotlin multiplatform projects.

Upvotes: 2

Views: 1178

Answers (1)

jaro2gw
jaro2gw

Reputation: 133

Issue seems to be obsolete because of the introduction of the project wizard for kotlin multiplatform projects directly in Intellij IDEA

A template project generated by this project wizard can be accessed here: https://github.com/jaro2gw/kotlin-multiplatform-full-stack

Upvotes: 1

Related Questions