Open bitspittle opened 1 year ago
Saw this in Youtrack but seems to be that Kotlin/JS team would not invest to this shortly. Vite seems to be good too.
Yeah! I'm aware of that issue and Kotlin/JS's lack of movement on it.
This is definitely something Kobweb can work around, and if we get it working, it would definitely be an additional perk to using Kobweb over vanilla Compose HTML.
There's already a case where the Kobweb Gradle plugin stomps over a mistake made by the Kotlin/JS team (around webpack config and live reloading, see here).
I experimented before and got pretty close to ripping out webpack and only using rollup, but I ran into issues around JS modules that I could never quite figure out, and since this wasn't critical path stuff, I had to cut my experiment short and move on.
I looked into Vite actually. Kobweb runs its own server so it doesn't need the webserver part of Vite, which just leaves the bundler, which is esbuild
.
I spent today exploring esbuild
(and also esbuild
+ terser
), comparing the speed and size of the final JS file it creates compared to what I'm currently getting with webpack (the default system provided by the Kotlin/JS folks)
I worked with a test project that, with webpack, was generating an output file at 937K, and esbuild
was 105K. It was a 12% increase in size. Applying terser
after the face resulted in a larger size??
esbuild
was FAST, but this initial exploration makes me think I'll be leaving things with webpack for now. I don't think speed matters too much with the export process, because it happens so rarely, and bundling is probably not the most significant, expensive step in an export anyway.
Full disclosure, I'm far from an expert, so maybe I configured something wrong. I'll include the code below I used as the way I explored this issue.
import com.varabyte.kobweb.gradle.application.util.configAsKobwebApplication
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExec
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.jetbrains.compose)
id("com.varabyte.kobweb.application")
id("com.varabyte.kobwebx.markdown")
}
group = "playground"
version = "1.0-SNAPSHOT"
kobweb {
markdown {
imports.add(".components.widgets.*")
}
kspProcessorDependency.set("com.varabyte.kobweb:project-processors")
}
kotlin {
configAsKobwebApplication(includeServer = true)
sourceSets {
val commonMain by getting {
dependencies {
implementation(compose.runtime)
}
}
val jsMain by getting {
dependencies {
implementation(compose.html.core)
implementation("com.varabyte.kobweb:kobweb-core")
implementation("com.varabyte.kobweb:kobweb-silk")
implementation("com.varabyte.kobwebx:silk-icons-fa")
implementation("com.varabyte.kobwebx:kobwebx-markdown")
implementation(project(":sitelib"))
implementation(devNpm("esbuild", "0.19.4"))
implementation(devNpm("terser", "5.20.0"))
}
}
val jvmMain by getting {
dependencies {
implementation("com.varabyte.kobweb:kobweb-api")
implementation(project(":sitelib"))
}
}
}
}
val mainCompilation = kotlin.js().compilations.findByName("main")
.let {
checkNotNull(it) { "Could not find main compilation" }
}
val esbuildBundleTask = NodeJsExec.create(mainCompilation, "esbuildBundle") {
group = "build"
description = "???"
val esbuildPath = project.rootProject.layout.buildDirectory
.file("js/node_modules/.bin/esbuild")
.get().asFile.absolutePath
val kotlinJsOutputDir = project.layout.buildDirectory.dir("compileSync/js/main/productionExecutable/kotlin").get()
val esbuildOutputFile = project.layout.buildDirectory.file("esbuild/output.js").get()
inputs.dir(kotlinJsOutputDir)
outputs.file(esbuildOutputFile)
this.nodeArgs = mutableListOf(
esbuildPath,
"--bundle",
"--minify",
kotlinJsOutputDir.file("playground.js").asFile.absolutePath,
"--outfile=${esbuildOutputFile.asFile.absolutePath}")
println("Generating ${esbuildOutputFile.asFile.absolutePath}.")
}
NodeJsExec.create(mainCompilation, "terserDce") {
group = "build"
description = "???"
val terserPath = project.rootProject.layout.buildDirectory
.file("js/node_modules/.bin/terser")
.get().asFile.absolutePath
dependsOn(esbuildBundleTask)
val terserOutputFile = project.layout.buildDirectory.file("terser/output.js").get()
val esbuildBundleOutput = esbuildBundleTask.get().outputs.files.singleFile
inputs.file(esbuildBundleOutput)
outputs.file(terserOutputFile)
this.nodeArgs = mutableListOf(
terserPath,
esbuildBundleOutput.absolutePath,
"--output",
terserOutputFile.asFile.absolutePath,
)
println("Generating ${terserOutputFile.asFile.absolutePath}.")
}
see kotlin-vite new plugin: https://gitlab.com/opensavvy/automation/kotlin-vite
bun would be an excellent choice. i am working with it in nextjs typescript projects and it's deffo a good choice
Thanks @christian-draeger ! I've heard good things. Still not sure what it would take to integrate a different bundler inside a Kotlin project but I will be curious to try.
Right now, Kobweb uses Webpack to bundle JS code because, well, this is the automatic solution provided by the JB Compose Plugin.
Bun recently took the JS world by storm, and they are promising an insanely fast bundler: https://bun.sh/blog/bun-bundler
There's a chance with Bun we could simultaneously drop export compile times down significantly while also, maaaaybe, ending up with smaller JS files? Worth investigating!
esbuild
might also be a solution here.As far as I understand it, we don't actually need most of what Webpack offers since Kobweb runs its own web server directly.
To get this to work, you'd need to look into the Kobweb Gradle Application Plugin, see how it's currently triggering webpack tasks, disable that path, and slip in a different bundler instead.