From 75b583a1045b10cc62974a282aeed896ae7f1244 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Fri, 16 Feb 2024 00:10:14 +0100 Subject: [PATCH 1/8] Update to Kotlin 1.9.22 and migrate to Kotlin/Wasm --- README.md | 4 ++-- build.gradle.kts | 19 +++++++++++++++++-- src/{main => wasmJsMain}/kotlin/main.kt | 7 ++++--- src/{main => wasmJsMain}/resources/index.html | 0 4 files changed, 23 insertions(+), 7 deletions(-) rename src/{main => wasmJsMain}/kotlin/main.kt (61%) rename src/{main => wasmJsMain}/resources/index.html (100%) diff --git a/README.md b/README.md index d30f247..070d7e7 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,13 @@ Further documentation for Wrangler can be found [here](https://developers.cloudf After setting up Kotlin per the linked instructions above, ``` -./gradlew :compileProductionExecutableKotlinJs +./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. ``` -wrangler publish build/js/packages/kotlin-worker-hello-world/kotlin/kotlin-worker-hello-world.js +wrangler publish build/js/packages/kotlin-worker-hello-world-wasm-js/kotlin/kotlin-worker-hello-world-wasm-js.mjs ``` 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) diff --git a/build.gradle.kts b/build.gradle.kts index e31f80e..297acf1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,8 @@ +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension +import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask + plugins { - kotlin("js") version "1.7.10" + kotlin("multiplatform") version "1.9.22" } group = "org.example" @@ -10,9 +13,21 @@ repositories { } kotlin { - js(IR) { + wasmJs { nodejs { } binaries.executable() + + // Uncomment the next line to apply Binaryen and get optimized wasm binaries + // applyBinaryen() } } + +// To run in Node.js +rootProject.the().apply { + nodeVersion = "22.0.0-nightly2024021536dcd399c0" + nodeDownloadBaseUrl = "https://nodejs.org/download/nightly" +} +tasks.withType().configureEach { + args.add("--ignore-engines") +} diff --git a/src/main/kotlin/main.kt b/src/wasmJsMain/kotlin/main.kt similarity index 61% rename from src/main/kotlin/main.kt rename to src/wasmJsMain/kotlin/main.kt index dd8d0a6..4ea4f0c 100644 --- a/src/main/kotlin/main.kt +++ b/src/wasmJsMain/kotlin/main.kt @@ -1,3 +1,4 @@ +import org.w3c.fetch.Headers import org.w3c.fetch.Request import org.w3c.fetch.Response import org.w3c.fetch.ResponseInit @@ -5,10 +6,10 @@ 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", + "Kotlin Worker hello world".toJsString(), ResponseInit(headers = headers) ) } diff --git a/src/main/resources/index.html b/src/wasmJsMain/resources/index.html similarity index 100% rename from src/main/resources/index.html rename to src/wasmJsMain/resources/index.html From 0e2801f8bec5dd71532229cf509fb1aacfe00881 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Tue, 19 Mar 2024 22:16:39 +0100 Subject: [PATCH 2/8] Update README.md Co-authored-by: Brendan Irvine-Broque --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 070d7e7..124bfc7 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,10 @@ After setting up Kotlin per the linked instructions above, ./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 and package it into a JavaScript executable, after which you can run `wrangler deploy` to push it to Cloudflare. ``` -wrangler publish build/js/packages/kotlin-worker-hello-world-wasm-js/kotlin/kotlin-worker-hello-world-wasm-js.mjs +npx wrangler@latest deploy build/js/packages/kotlin-worker-hello-world-wasm-js/kotlin/kotlin-worker-hello-world-wasm-js.mjs ``` 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) From 3f690f12c524f34abe5b93ebc40da35bc1ec8d2e Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Tue, 19 Mar 2024 23:56:33 +0100 Subject: [PATCH 3/8] Update a response message --- src/wasmJsMain/kotlin/main.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasmJsMain/kotlin/main.kt b/src/wasmJsMain/kotlin/main.kt index 4ea4f0c..f49acd9 100644 --- a/src/wasmJsMain/kotlin/main.kt +++ b/src/wasmJsMain/kotlin/main.kt @@ -9,7 +9,7 @@ fun fetch(request: Request) : Response { val headers = Headers() headers.append("content-type", "text/plain") return Response( - "Kotlin Worker hello world".toJsString(), + "Hello from Kotlin/Wasm Worker".toJsString(), ResponseInit(headers = headers) ) } From c039bfc2726bf0b1a647732f8dba36fd91221347 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Wed, 20 Mar 2024 00:01:38 +0100 Subject: [PATCH 4/8] Make mjs file generated by Kotlin compiler compatible with Cloudflare Workers --- build.gradle.kts | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 297acf1..3986dd5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,6 @@ 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("multiplatform") version "1.9.22" @@ -18,8 +19,48 @@ kotlin { } binaries.executable() - // Uncomment the next line to apply Binaryen and get optimized wasm binaries - // applyBinaryen() + // Comment the next line to turn off optimization by Binaryen + applyBinaryen() + + compilations + .configureEach { + binaries.withType().configureEach { + linkTask.configure { + val moduleName = linkTask.flatMap { + it.compilerOptions.moduleName + } + val fileName = moduleName.map { module -> + "$module.uninstantiated.mjs" + } + + val mjsFile: Provider = 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 \\(!isNodeJs && !isStandaloneJsVM && !isBrowser\\) \\{[^\\}]*\\}".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) + } + } + } + } } } From 4172579e8f7d41fa19372fa6316e5c59aea9eb39 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Wed, 20 Mar 2024 00:01:48 +0100 Subject: [PATCH 5/8] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 124bfc7..8abf6f4 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,11 @@ After setting up Kotlin per the linked instructions above, ./gradlew :compileProductionExecutableKotlinWasmJs ``` -That will compile your code and package it into a JavaScript executable, after which you can run `wrangler deploy` to push it to Cloudflare. +That will compile your code and package it into a WebAssembly executable and JavaScript glue code, +after which you can run `wrangler deploy` to push it to Cloudflare. ``` npx wrangler@latest deploy build/js/packages/kotlin-worker-hello-world-wasm-js/kotlin/kotlin-worker-hello-world-wasm-js.mjs ``` -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) +For more information on interop between Kotlin and JavaScript, see the [Kotlin docs](https://kotlinlang.org/docs/wasm-js-interop.html). \ No newline at end of file From a34ec3233e089a827ef36c4b923b1af31ad52b24 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Wed, 27 Mar 2024 00:23:14 +0100 Subject: [PATCH 6/8] Add a reference to an issue about supporting Workers in mjs out of the box --- build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle.kts b/build.gradle.kts index 3986dd5..62d511e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -22,6 +22,7 @@ kotlin { // Comment the next line to turn off optimization by Binaryen applyBinaryen() + // Workaround for https://youtrack.jetbrains.com/issue/KT-66972 compilations .configureEach { binaries.withType().configureEach { From 215c515790b3df320f8e5618f2ab0472b760f8b3 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Thu, 28 Mar 2024 00:26:27 +0100 Subject: [PATCH 7/8] Update README.md * Change wording. * Update some links. * Add some other useful links. --- README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8abf6f4..1a4a92b 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # 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 @@ -10,19 +10,22 @@ Configure the [wrangler.toml](wrangler.toml) by filling in the `account_id` from 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: -``` +```shell ./gradlew :compileProductionExecutableKotlinWasmJs ``` -That will compile your code and package it into a WebAssembly executable and JavaScript glue code, -after which you can run `wrangler deploy` 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-wasm-js.mjs ``` -For more information on interop between Kotlin and JavaScript, see the [Kotlin docs](https://kotlinlang.org/docs/wasm-js-interop.html). \ No newline at end of file +## Learn more + +* [Kotlin/Wasm Overview](https://kotl.in/wasm/) +* [Kotlin/Wasm JavaScript interop](https://kotlinlang.org/docs/wasm-js-interop.html). From 7c244b1989466554920d1108893ca9be2ac1efee Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Fri, 12 Apr 2024 01:33:15 +0200 Subject: [PATCH 8/8] Fix compatability with the latest wrangler version Since ~3.46.0 wrangler is modifying an exported object, but wasm instance.exports is not modifiable, and it leads to runtime error. Also: * Make it compatible with both Kotlin 1.9.2x and upcoming 2.0.0. * For 2.0.0 don't use default export to avoid errors in the console. --- README.md | 2 +- build.gradle.kts | 3 ++- src/wasmJsMain/resources/kotlin-worker-hello-world.js | 5 +++++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 src/wasmJsMain/resources/kotlin-worker-hello-world.js diff --git a/README.md b/README.md index 1a4a92b..d036c05 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ That will compile your code into a WebAssembly executable and JavaScript glue co 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-wasm-js.mjs +npx wrangler@latest deploy build/js/packages/kotlin-worker-hello-world-wasm-js/kotlin/kotlin-worker-hello-world.js ``` ## Learn more diff --git a/build.gradle.kts b/build.gradle.kts index 62d511e..3279a5a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,6 +4,7 @@ import org.jetbrains.kotlin.gradle.targets.js.ir.* plugins { kotlin("multiplatform") version "1.9.22" + // kotlin("multiplatform") version "2.0.0-RC1" } group = "org.example" @@ -43,7 +44,7 @@ kotlin { val file = mjsFile.get().asFile val text = file.readText() val newText = text - .replace("if \\(!isNodeJs && !isStandaloneJsVM && !isBrowser\\) \\{[^\\}]*\\}".toRegex(), "") + .replace("if \\(!\\w+( && !\\w+)*\\) \\{[^\\}]*\\}".toRegex(), "") .replace( "(if \\(isBrowser\\) \\{\\s*wasmInstance[^\\}]*\\})".toRegex(), """ diff --git a/src/wasmJsMain/resources/kotlin-worker-hello-world.js b/src/wasmJsMain/resources/kotlin-worker-hello-world.js new file mode 100644 index 0000000..b166f0f --- /dev/null +++ b/src/wasmJsMain/resources/kotlin-worker-hello-world.js @@ -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; \ No newline at end of file