Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to Kotlin 1.9.22 and migrate to Kotlin/Wasm #6

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
# Kotlin hello world for Cloudflare Workers

Your Kotlin code in [main.kt](https://github.com/cloudflare/kotlin-worker-hello-world/blob/master/src/main/kotlin/main.kt), running on Cloudflare Workers
Your Kotlin code in [main.kt](src/wasmJsMain/kotlin/main.kt), running on Cloudflare Workers

In addition to [Wrangler v2.x](https://github.com/cloudflare/wrangler2) you will need to install Kotlin, including a JDK and support for Gradle projects. The easiest way to do this is using the free Community Edition of [IntelliJ IDEA](https://kotlinlang.org/docs/tutorials/jvm-get-started.html).
In addition to [Wrangler v2.x](https://github.com/cloudflare/wrangler2) you will need to install a JDK 8 or newer. [The easiest way to do this](https://www.jetbrains.com/guide/java/tips/download-jdk/) is using the free Community Edition of [IntelliJ IDEA](https://www.jetbrains.com/idea/download/).

## Wrangler

Configure the [wrangler.toml](wrangler.toml) by filling in the `account_id` from the Workers pages of your Cloudflare Dashboard.

Further documentation for Wrangler can be found [here](https://developers.cloudflare.com/workers/tooling/wrangler).

## Gradle
## Build & Deploy

After setting up Kotlin per the linked instructions above,
After setting up your environment, run the following command:

```
./gradlew :compileProductionExecutableKotlinJs
```shell
./gradlew :compileProductionExecutableKotlinWasmJs
```

That will compile your code and package it into a JavaScript executable, after which you can run `wrangler publish` to push it to Cloudflare.
That will compile your code into a WebAssembly executable and JavaScript glue code,
after which you can run `wrangler deploy` to push it to Cloudflare:

```shell
npx wrangler@latest deploy build/js/packages/kotlin-worker-hello-world-wasm-js/kotlin/kotlin-worker-hello-world.js
```
wrangler publish build/js/packages/kotlin-worker-hello-world/kotlin/kotlin-worker-hello-world.js
```

For more information on interop between Kotlin and Javascript, see the [Kotlin docs](https://kotlinlang.org/docs/reference/js-interop.html). Regarding coroutines, see [this issue and workaround](https://github.com/cloudflare/kotlin-worker-hello-world/issues/2)
## Learn more

* [Kotlin/Wasm Overview](https://kotl.in/wasm/)
* [Kotlin/Wasm JavaScript interop](https://kotlinlang.org/docs/wasm-js-interop.html).
62 changes: 60 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask
import org.jetbrains.kotlin.gradle.targets.js.ir.*

plugins {
kotlin("js") version "1.7.10"
kotlin("multiplatform") version "1.9.22"
// kotlin("multiplatform") version "2.0.0-RC1"
}

group = "org.example"
Expand All @@ -10,9 +15,62 @@ repositories {
}

kotlin {
js(IR) {
wasmJs {
nodejs {
}
binaries.executable()

// Comment the next line to turn off optimization by Binaryen
applyBinaryen()

// Workaround for https://youtrack.jetbrains.com/issue/KT-66972
compilations
.configureEach {
binaries.withType<Executable>().configureEach {
linkTask.configure {
val moduleName = linkTask.flatMap {
it.compilerOptions.moduleName
}
val fileName = moduleName.map { module ->
"$module.uninstantiated.mjs"
}

val mjsFile: Provider<RegularFile> = linkTask.flatMap {
it.destinationDirectory.file(fileName.get())
}

doLast {
val module = moduleName.get()
val file = mjsFile.get().asFile
val text = file.readText()
val newText = text
.replace("if \\(!\\w+( && !\\w+)*\\) \\{[^\\}]*\\}".toRegex(), "")
.replace(
"(if \\(isBrowser\\) \\{\\s*wasmInstance[^\\}]*\\})".toRegex(),
"""
$1

const isWorker = navigator.userAgent.includes("Workers")
if (isWorker) {
const { default: wasmModule } = await import('./$module.wasm');
wasmInstance = (await WebAssembly.instantiate(wasmModule, importObject));
}
""".trimIndent()
)

file.writeText(newText)
}
}
}
}
}
}

// To run in Node.js
rootProject.the<NodeJsRootExtension>().apply {
nodeVersion = "22.0.0-nightly2024021536dcd399c0"
nodeDownloadBaseUrl = "https://nodejs.org/download/nightly"
}
tasks.withType<KotlinNpmInstallTask>().configureEach {
args.add("--ignore-engines")
}
7 changes: 4 additions & 3 deletions src/main/kotlin/main.kt → src/wasmJsMain/kotlin/main.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import org.w3c.fetch.Headers
import org.w3c.fetch.Request
import org.w3c.fetch.Response
import org.w3c.fetch.ResponseInit

@OptIn(ExperimentalJsExport::class)
@JsExport
fun fetch(request: Request) : Response {
val headers: dynamic = object {}
headers["content-type"] = "text/plain"
val headers = Headers()
headers.append("content-type", "text/plain")
return Response(
"Kotlin Worker hello world",
"Hello from Kotlin/Wasm Worker".toJsString(),
ResponseInit(headers = headers)
)
}
File renamed without changes.
5 changes: 5 additions & 0 deletions src/wasmJsMain/resources/kotlin-worker-hello-world.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as foo from "./kotlin-worker-hello-world-wasm-js.mjs"

let keys = Object.keys(foo);

export default keys.length === 1 && keys[0] === "default" ? { ... foo.default } : foo;