alexandre mommers
09/16/2025, 10:07 PMtapchicoma
09/17/2025, 7:37 AMjs()
and wasmJs()
targets
cc @Artem KobzarMichael Paus
09/17/2025, 8:15 AMArtem Kobzar
09/17/2025, 10:32 AMwasmJs
has. Ideally, any wasmJsMain
after renaming to webMain
should work (with the only work should done: add import kotlin.js.*
everywhere where some interop types like JsAny
are used)Michael Paus
09/17/2025, 11:26 AMimport kotlinx.coroutines.promise
cannot be resolved anymore. I use the promise
extension function on CoroutineScope
. This worked for wasmJs but does not seem to work anymore when I add JS.Artem Kobzar
09/17/2025, 11:35 AMwebMain
and it should work, but the work should be done on the kotlinx.coroutines side.
What you can do for such cases as a work-around till it's fixed, is to add expect declaration in the web
source set, and actualize it in the same way in wasmJs
and js
source sets.Michael Paus
09/17/2025, 11:48 AMDmitry Khalanskiy [JB]
09/17/2025, 12:07 PMpromise
functions have different signatures and so belong to different source sets.
• On JS, the promise
can return a Promise<T>
for any Kotlin type T
.
• On Wasm/JS, it's limited to returning Promise<T>
for T
that implement JsAny?
, which does not include arbitrary Kotlin types.
In a shared signature, we would have to either return Promise<JsAny?>
, losing the type information on JS, or limit T
to JsAny?
, preventing promises returning non-JS types on Wasm/JS.
If you see a way to maintain the flexibility of the signatures and still unify them, we'll gladly accept the proposed fix, of course!Artem Kobzar
09/17/2025, 12:13 PMweb
source-set (that we've just introduced) the signature could be the same:
public fun <T: JsAny?> CoroutineScope.promise(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Promise<T>
JsAny
is actualized in Wasm as an external interface, and in JS as Any
typeDmitry Khalanskiy [JB]
09/17/2025, 12:15 PMpromise<Boolean> { true }
, for example, or even promise<Unit> { }
.Artem Kobzar
09/17/2025, 12:17 PMJsBoolean
and Boolean
are not in a type relation for WasmJSDmitry Khalanskiy [JB]
09/17/2025, 12:17 PMArtem Kobzar
09/17/2025, 12:18 PMArtem Kobzar
09/17/2025, 12:19 PM// webMain
expect public fun <T> CoroutineScope.promise(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Promise<JsAny?> // just repeat WasmJs signature
// wasmJsMain
actual public fun <T> CoroutineScope.promise(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Promise<JsAny?> // remains the same
// jsMain
actual public fun <T> CoroutineScope.promise(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Promise<T> // not sure if there isn't something like `incompatible actual return type` compilation error
Dmitry Khalanskiy [JB]
09/17/2025, 12:29 PMe: file:///home/dkhalansky/IdeaProjects/kotlinx.coroutines/kotlinx-coroutines-core/js/src/Promise.kt:21:38 [ACTUAL_WITHOUT_EXPECT] 'actual fun <T> CoroutineScope.promise(context: CoroutineContext, start: CoroutineStart, block: suspend CoroutineScope.() -> T): Promise<T>' has no corresponding expected declaration
The following declaration is incompatible because return type is different:
expect fun <T> CoroutineScope.promise(context: CoroutineContext = ..., start: CoroutineStart = ..., block: suspend CoroutineScope.() -> T): Promise<JsAny?>
Artem Kobzar
09/17/2025, 12:33 PMMichael Paus
09/17/2025, 1:14 PM@kotlin.js.ExperimentalWasmJsInterop
on the JS side. Maybe this should also be renamed to ExperimentalWebInterop then.Artem Kobzar
09/17/2025, 1:25 PMMichael Paus
09/17/2025, 1:46 PMArtem Kobzar
09/17/2025, 2:40 PMturansky
09/17/2025, 3:06 PMCloneable
- problem of IDEAturansky
09/17/2025, 3:06 PMturansky
09/17/2025, 3:11 PMPromise.await()
2. Promise.asDeferred()
3. CoroutineScope.promise(...)
already available in webMain
😉turansky
09/17/2025, 3:13 PMMichael Paus
09/17/2025, 3:16 PMturansky
09/17/2025, 3:31 PM@kotlin.js.ExperimentalWasmJsInterop
by defaultMichael Paus
09/18/2025, 9:37 AMWhen accessing module declarations from UMD, they must be marked with both @JsModule and @JsNonModule.
Here is a shortened version of that code.
private suspend fun xxx(fs: FileSystem, filePath: Path) {
JSZip().apply {
val bytes = filePath.readBytes(fs)
file(filePath.toString(), bytes.toUint8Array())
}
}
The critical line is the one which starts with JSZip. It was a bit confusing that IJ indicates a different (false) error than the actual compiler error.
All in all I think that the common web folder is still good for some surprises 😢.turansky
09/18/2025, 9:42 AMtarget = "es2015"
turansky
09/18/2025, 9:43 AMMichael Paus
09/18/2025, 9:57 AMtasks.withType<KotlinJsCompile>().configureEach {
compilerOptions {
target = "es2015"
}
}
to the build file, right?
After adding this the code does compile but what does that mean in practice now? Do I have to compile all my modules with that option now in order to ensure compatibility or not? What other compatibility problems are still lingering around?
Switching to the new common web folder is full of undocumented surprises 🤨.turansky
09/18/2025, 10:17 AMtarget = "es2015"
- default, which we miss 😞turansky
09/18/2025, 10:18 AMturansky
09/18/2025, 10:18 AMturansky
09/18/2025, 10:22 AMDo I have to compile all my modules with that option now in order to ensure compatibility or not?We apply it for all subprojects. Reasons: 1. No legacy inspections 2. Tests, compiled in legacy mode will test something else
turansky
09/18/2025, 10:23 AMMichael Paus
09/18/2025, 12:14 PMArtem Kobzar
09/18/2025, 12:35 PMalexandre mommers
09/19/2025, 12:01 AMArtem Kobzar
09/19/2025, 6:39 AMMichael Paus
09/19/2025, 6:51 AMArtem Kobzar
09/19/2025, 6:55 AM