Is ktor not compatible to ESM? I created a minimal...
# ktor
v
Is ktor not compatible to ESM? I created a minimal KMP project with build script
Copy code
plugins {
    kotlin("multiplatform") version "2.0.21"
}

repositories {
    mavenCentral()
}

kotlin {
    js {
        useEsModules()
        binaries.executable()
        nodejs()
    }

    sourceSets {
        jsMain {
            dependencies {
                implementation(dependencies.platform("io.ktor:ktor-bom:3.0.0"))
                implementation("io.ktor:ktor-client-core")
                implementation("io.ktor:ktor-client-js")
            }
        }
    }
}
and
Main.kt
Copy code
import io.ktor.client.HttpClient
import io.ktor.client.engine.js.Js
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import io.ktor.http.isSuccess

suspend fun main() {
    runCatching {
        val client = HttpClient(Js)
        val response = client.get("<https://echo.free.beeceptor.com/api/GetFiles>")
        if (response.status.isSuccess()) {
            println("Request:\n${response.bodyAsText()}")
        } else {
            println("Could not get response (statusCode: ${response.status.value} / statusMessage: ${response.status.description})")
        }
    }.onFailure {
        it.printStackTrace()
    }
}
but when I execute the task
:jsNodeProductionRun
, I get
Copy code
ReferenceError: require is not defined
    at eval (eval at AbortController_0 (file:///D:/mnt/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/js/src/io/ktor/client/engine/js/compatibility/Utils.kt:44:33), <anonymous>:1:1)
    at AbortController_0 (file:///D:/mnt/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/js/src/io/ktor/client/engine/js/compatibility/Utils.kt:44:33)
    at $executeCOROUTINE$20.protoOf.a8 (file:///D:/mnt/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/js/src/io/ktor/client/engine/js/JsClientEngine.kt:45:26)
    at JsClientEngine.protoOf.h22 (file:///D:/Sourcecode/other/others/showcase/build/js/packages/showcase/kotlin/ktor-ktor-client-ktor-client-core.mjs:6716:14)
    at HttpClientEngine$executeWithinCallContext$slambda.protoOf.a8 (file:///D:/mnt/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/engine/HttpClientEngine.kt:100:13)
    at HttpClientEngine$executeWithinCallContext$slambda.protoOf.g22 (file:///D:/Sourcecode/other/others/showcase/build/js/packages/showcase/kotlin/ktor-ktor-client-ktor-client-core.mjs:1404:14)
    at l (file:///D:/Sourcecode/other/others/showcase/build/js/packages/showcase/kotlin/ktor-ktor-client-ktor-client-core.mjs:1452:14)
    at _no_name_provided__qut3iv_0.protoOf.a8 (file:///D:/Sourcecode/other/others/showcase/build/js/packages/showcase/kotlin/js/src/kotlin/coroutines/intrinsics/IntrinsicsJs.kt:179:40)
    at _no_name_provided__qut3iv_0.protoOf.z7 (file:///D:/Sourcecode/other/others/showcase/build/js/packages/showcase/kotlin/src/kotlin/util/Standard.kt:50:44)
    at _no_name_provided__qut3iv_0.protoOf.d8 (file:///D:/Sourcecode/other/others/showcase/build/js/packages/showcase/kotlin/kotlin-kotlin-stdlib.mjs:4266:15)
If I change from
useEsModules()
to
useCommonJs()
in the build script it works, but that is not an option in the actual project.
a
Unfortunately, this is a long-standing issue occurring when ES Modules are used.
t
@Vampire if you need pill - I have it πŸ™‚. If you are ready to fix issue (fix is simple in fact) - I can assist πŸ˜‰
v
I'm interested in both. But if the pill is
compilerOptions.target = "es2015"
as mentioned in that linked ticket, that unfortunately does not change anything, I just tried it.
t
Pill - remove
require
calls and just use classes in compiled JS files.
Fix - the same, but in Kotlin files
v
Ok, I don't think I attempt the fix. Even trying to sync the ktor project to IDE eats tens of Gigabytes until I have 0 bytes left on disk. Besides that with only 4 occurrences but still being such a long-standing issue it appears to be somewhat complex to get it fixed properly for all cases. Do you have a helping hand for the
pill
? I tried with
Copy code
tasks.withType<IncrementalSyncTask>().configureEach {
    doLast {
        outputs
            .files
            .asFileTree
            .filter { it.name == "ktor-ktor-client-ktor-client-core.mjs" }
            .forEach {
                it
                    .readText()
                    .replace("var controller = eval('require')('abort-controller');", "")
                    .replace("tmp = new controller();", "tmp = new AbortController();")
                    .replace("jsRequireNodeFetch()(", "fetch(")
                    .apply(it::writeText)
            }
    }
}
but there is probably something wrong with it. It bypasses the "require" errors, but then complains that "this.a2w_1.on is not a function" which is the
body.on
calls in
NodeFetch.kt
of ktor.
t
Copy code
replace("eval('require')('abort-controller');", "globalThis.AbortController")
I expect such replaces
v
Ok, so in case I got you right, I have now
Copy code
it
    .readText()
    .replace("eval('require')('abort-controller')", "globalThis.AbortController")
    .replace("eval('require')('node-fetch')", "globalThis.fetch")
    .apply(it::writeText)
which gives the same problem from the last line of the my last comment
😐 1
t
v
Nice thanks. πŸ™‚ Just that your PR has the exact same problem I described.
image.png
I guess it uses the "wrong"
fetch
and thus the body in the response does not have the expected method or something like that
Besides that the PR is not caring about the
ws
require and the
crypto
require. Those are not in my replacements because I didn't hit them so far and just replaced what I got an error about. πŸ™‚
t
Which part of Ktor do you use?
v
Currently only as http client as shown in the original post.
t
v
You mean using that instead of ktor? That would be a sad "solution" πŸ˜• In my actual project I for now downgraded null-writable to v1 and thus was able to switch back to CJS where ktor works.
t
Just that your PR has the exact same problem I described.
PR updated πŸ˜‰
v
I'm curious whether your PR in the end will be accepted. I'd expect that it was intentional that on node different code was used than in the browser. πŸ€·β€β™‚οΈ Anyway, the update made it worse, now it does not even compile anymore. πŸ˜„
Argh, and "In my actual project I for now downgraded null-writable to v1 and thus was able to switch back to CJS where ktor works." is not as easy either. After sending through
ncc
the result produces "Error: Cannot find module 'abort-controller'". It only worked when using the compile result before sending through
ncc
. πŸ™„
t
> now it does not even compile anymore Fixed πŸ˜‰
I'd expect that it was intentional that on node different code was used than in the browser. πŸ€·β€β™‚οΈ
fetch
exists in Node since Node 18. JS also want be "multiplatform" πŸ˜‰
v
Ah, I see, so it is mainly for backwards compatibility with older node versions. Anyway, I also solved the ncc problem with replacing
eval('require')(
by
require(
in the compile result before sending it through ncc, that allows it to pack properly and it seems to work so far. :-)
Thanks for your work. I gave ESM another shot now. Thanks to your PR I could get it to work with this:
Copy code
.replace("eval('require')('abort-controller')", "globalThis.AbortController")
.replace("eval('require')('node-fetch')", "globalThis.fetch")
.replace("function readBodyNode(", "function _readBodyNode(")
.replace(" readBodyNode(", " readBodyBrowser(")
as I'm currently not hitting the other two `require`s. πŸ™‚
πŸ‘ 1