logging values in suspending functions seems to lo...
# javascript
b
logging values in suspending functions seems to log the wrapping continuation object; is there a way to change that? Expected value would have been [1, 2]; Using println() works btw but that one calls toString()
1
e
That's most probably a bug. Another issue I suppose!
b
thank you, been filing one each day xD
e
Yeah I know the feeling. But better log it today than tomorrow
b
will do, thanks
e
Wait a second before creating it, I want to try it out
Do you have a snippet? I can't reproduce.
b
one sec, need to provide tech support
ok, found the issue: I was using listOf isntead of arrayOf, stupid muscle memory
✔️ 1
thank you nonetheless
e
Which version of Kotlin are you using?
b
the latest beta
2.0.20-Beta2
I think
yes
e
I can't reproduce on 2.0.20-Beta2
b
hm, maybe a gradle thing?
e
Clean + disable caching
b
can reproduce it here
Main.kt:21 Uncaught (in promise) ReferenceError: Options is not defined at Cn.f4 (Main.kt2137) at Cn.qi (main.js181406) at Wu (main.js182102) at Cn.f4 (IntrinsicsJs.kt17940) at Cn.e4 (Standard.kt5044) at Cn.i4 (main.js131102) at Cn.vb (Continuation.kt455) at Cn.kg (JSDispatcher.kt12725) at JSDispatcher.kt5538
e
Are you using the same version for the plain objects plugin?
b
Copy code
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.await
import kotlinx.coroutines.promise
import kotlinx.js.JsPlainObject
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.js.Promise

@JsPlainObject
external interface Options {
    val method: String
}

external fun fetch(url: String, options: Options): Promise<String>

suspend fun getMethod(): String = "GET"

fun main() {
    CoroutineScope(EmptyCoroutineContext).promise {
        fetch("<https://google.com>", Options(method = getMethod())).await()
    }
}
t
Do you declare custom
fetch
? Or it's just for test case?
b
just a test
I needed an existing function on scope to test it
t
Good catch with
suspend
call 😜
b
when I move the suspend call out of the object constructor, I'm getting a CORS error (as expected)
e
After cloning the project, which task do I have to start?
b
build
./gradlew build with the contents in Main.kt that I pasted above
output is in dist/main.js btw
e
Compiles fine. How do I test it to reproduce the run time error?
b
I guess embed it in an HTML page
using a script tag
I'm loading it via module.json in FoundryVTT but that's a proprietary electron app
you could also just grep the compiled js and look for "Options"
that should not be inlined in the js code at all
e
Being it targets Electron, is there a reason why you're enabling
browser()
instead of
nodejs()
?
b
it's loaded on the client side
I don't have access to backend code
runs in Chromium I think
e
Using the ES2015 target should make it work.
t
With
target = "es2015"
new
suspend
processing (on JS generators) will be used
e
Just tested and it works. Would be nice to understand the option combination that breaks it
👍 1
thank you color 1
b
will report back, gotta go
e
Looks like it's the Webpack minification that breaks it
Reproduced without minification and using the Node configuration
With:
Copy code
val options = Options(method = getMethod())
fetch("<https://google.com>", options).await()
result:
Copy code
switch (tmp) {
  case 0:
    this.set_exceptionState_fex74n_k$(3);
    var tmp_0 = this;
    tmp_0.this0__1 = Options;
    this.set_state_rjd8d0_k$(1);
    suspendResult = getMethod(this);
    if (suspendResult === get_COROUTINE_SUSPENDED()) {
      return suspendResult;
    }
    continue $sm;
  case 1:
    this.method1__1 = suspendResult;
    var tmp_1 = this;
    this.this0__1;
    tmp_1.options2__1 = {method: this.method1__1};
    this.set_state_rjd8d0_k$(2);
    suspendResult = await_0(fetch('<https://google.com>', this.options2__1), this);
With:
Copy code
val method = getMethod()
val options = Options(method = method)
fetch("<https://google.com>", options).await()
result:
Copy code
switch (tmp) {
  case 0:
    this.set_exceptionState_fex74n_k$(3);
    this.set_state_rjd8d0_k$(1);
    suspendResult = getMethod(this);
    if (suspendResult === get_COROUTINE_SUSPENDED()) {
      return suspendResult;
    }
    continue $sm;
  case 1:
    this.method0__1 = suspendResult;
    var tmp_0 = this;
    tmp_0.options1__1 = {method: this.method0__1};
    this.set_state_rjd8d0_k$(2);
    suspendResult = await_0(fetch('<https://google.com>', this.options1__1), this);
Note the strange
tmp_0.this0__1 = Options;
I've attached a self-contained reproducer to the YT issue.
b
thank you !
is there something similar to TypeScript's interface merging? like having multiple libraries that define extensions on the window object and having everything merged together correctly?
t
Extensions?
b
I see, just define extension properties then
like this?
Copy code
external interface PF2EGame {
    val actions: JsMap<String, String>
}

inline val Game.pf2e: PF2EGame
    get() = asDynamic().pf2e
ai autocompleted the getter so not sure (also note the inline instead of external)
t
Yes
It also can be
Copy code
@JsExtension
external val Game.pf2e: PF2EGame
This issue required (more votes)
b
how do you do external companion objects? looking at stuff like
Copy code
external interface Factory<T> {
    fun create(): T
}

external class Test {
    companion object : Factory<Test>
}

external class Test2: Test {
    companion object: Factory<Test2>
}
where the JS code inherits those static create methods
Copy code
class Test {
    static create() {
        return new this()
    }
}

class Test2 extends Test {}

console.log(Test2.create())  // prints Test2 {}
e
You can't create hierarchies of companion objects.
b
Copy code
external interface Factory<T> {
    fun create(): T
}

open external class Test {
    companion object : Factory<Test> {
        override fun create(): Test
    }
}

external class Test2 : Test {
    companion object : Factory<Test2> {
        override fun create(): Test2
    }
}
I guess?
👌 1
t
And probably better hide
Factory
in
Test
It will be possible to use
Factory
in all child types
b
you mean nest it?
t
Yes, it can be helpful if members count grows - not your case
To avoid
create
redeclaration you can declare it as class
🙌 1
b
thank you!
t
with private constructor
b
Copy code
open external class Test {
    open class Factory<T> {
        fun create(): T
    }
    
    companion object : Factory<Test>
}

external class Test2 : Test {
    companion object : Factory<Test2>
}
t
@JsExternalInheritorsOnly
required
b
thank you
t
@JsName("prototype.constructor")
- can be useful in some cases
It will give you valid runtime reference
b
in what way?
you mean if you need to reference the mehtod?
t
Test.Factory doesn't exist in runtime
Test.prototype.constructor === Test // true
b
ah, I see
t
I don't see use cases
is/as
for companions probably won't work