is there a doc to show what we can do and the limi...
# multiplatform
a
is there a doc to show what we can do and the limit of the new "web" target from the 2.2.20 ?
👀 1
t
it is a shared source between
js()
and
wasmJs()
targets cc @Artem Kobzar
m
Yes, but what modifications have been done to the JS types and interop in general to make it usable in a common source set? I’d also like to see some documentation on that and especially if there are still things which do not work in common.
a
So, the limitations are the same as
wasmJs
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)
🙏 1
m
@Artem Kobzar When I do that and also add a JS target, then I get a compilation error because
import 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.
a
Hmm, interesting. Could you please add an issue to the kotlinx.coroutines and we will fix it, because it looks like a bug( What I see here, that the declaration just should be moved to the
webMain
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.
m
d
Hi! @Artem Kobzar, why do you believe this is a bug? These
promise
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!
a
@Dmitry Khalanskiy [JB] with the common
web
source-set (that we've just introduced) the signature could be the same:
Copy code
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
type
d
With the signature you propose, you won't be able to write
promise<Boolean> { true }
, for example, or even
promise<Unit> { }
.
a
In the common source set: yes In the Wasm source set: yes In the JS source set: no, I can And as far as I see, it's an expected behavior. since
JsBoolean
and
Boolean
are not in a type relation for WasmJS
d
Today, you can write this on Wasm/JS as well.
a
Oh, I see what you mean. I need to think about it a bit.
As a plan B, we can just introduce expect/actual like this:
Copy code
// 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
d
There is:
Copy code
e: 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?>
a
Hmm, it's sad. Anyway, I will think about how to unify this API (and I definitely need your help). Because I believe we all want kotlinx.coroutines to be usable from the web common source set.
💯 3
m
I tried to do the expect/actual variant with two identical implementations like the wasmJs one because I don’t need the greater flexibility of the current JS implementation. I am wondering though why IJ then tells me to opt in to
@kotlin.js.ExperimentalWasmJsInterop
on the JS side. Maybe this should also be renamed to ExperimentalWebInterop then.
a
@bashor ^^
m
After fixing the first issue via expect/actual I stumbled into the next one right away. This time its the ByteArray.toUint8Array() function from the Kotlin-Wrappers library which has a problem in the common web source set.
a
@turansky ^^
t
Problem with
Cloneable
- problem of IDEA
Compilation should be fine
And all coroutines integration with common
js.promise.Promise
is already introduced in Kotlin Wrappers. Here and here.
1.
Promise.await()
2.
Promise.asDeferred()
3.
CoroutineScope.promise(...)
already available in
webMain
😉
1 implementation for 2 platforms
m
Before anybody asks. I am using the latest version 2025.9.8 of the Kotlin wrappers 😇.
t
@Michael Paus in wrappers we added opt-in for
@kotlin.js.ExperimentalWasmJsInterop
by default
m
The code snippet above actually seems to have two independent problems. The “toUint8Array” error shown does indeed compile and is only shown as an error by IJ. However the code as a whole does not compile with the following message:
When accessing module declarations from UMD, they must be marked with both @JsModule and @JsNonModule.
Here is a shortened version of that code.
Copy 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 😢.
t
Please use
target = "es2015"
To avoid legacy inspections
m
You mean adding
Copy code
tasks.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 🤨.
t
target = "es2015"
- default, which we miss 😞
It gives you less bundle and more readable/synchronous and faster code
Do 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
m
I am starting to feel a little bit bad about having to unveil all these problems here. I just discovered that I cannot access the JS Ktor client engine anymore as soon as I move my code into a common web folder and activate both compilation targets (JS and wasmJs). If I only activate one of them in the build script the code compiles fine.
👀 1
a
@Michael Paus thank you a lot for sharing all of this. Since it's a fresh feature we would like to know about all of this ASAP, so we can fix it quickly. I've put it to my todo list to fix such a thing in KTOR
🙏 1
a
Why do we have projects like kotlinx-browser if everything is already on kotlin-wrappers?
👀 1
a
We needed a DOM library that contains a minimal number of wrappers (just externals, so it doesn't impact the bundle size much), is not opinionated, and preserves semantic versioning and binary backward compatibility for Compose Multiplatform. Additionally, it works well with both Wasm and JS (at the time we started, Kotlin-wrappers were not there) We're looking for some solutions to merge those libraries, and we're just at the beginning of the conversation.
m
I’d appreciate such a merger because I think the current situation is confusing for many people and can also lead to annoying conflicts if you have to use both libraries and then code completion is suggesting you incompatible versions of certain APIs. I am trying to remove usage of kotlinx-browser from my code wherever I can.
1
a
We fully understand the situation and really want to fix such a confusion. However, the differences in the source of truth for both of these libraries (TypeScript definitions for kotlin-wrappers and WebIDL for kotlinx-browser) lead to some really hard decisions for both parties, so we try to find compromises between the libraries' authors to move forward with a single solution.
👍 2