Hi, I have some experience with Kotlin Multiplatform on mobile, and recently I started getting into ...
c
Hi, I have some experience with Kotlin Multiplatform on mobile, and recently I started getting into the JS side of things. To me, the main point of KMP was, that I can share business logic between clients on different platforms, regardless of UI framework. I was surprised to find, that the JS documentation seemed to focus heavily on creating a React App with Kotlin. I don’t want to tie myself down to React, and be at the mercy of the maintainers of the Kotlin wrapper for it. That’s why I’m looking into KMP in the first place, instead of some cross-platform UI framework. With a lot of googling (I could not find official documentation on this?), I found that it is possible to create a package with Typescript annotations when building a Multiplatform library with
Copy code
js(IR) {
    browser()
    binaries.library()
}
which creates a folder inside the “build” folder called “productionLibrary”. This folder I could then import in my Angular App (or any other framework) with npm install absolutePathToProductionLibrary. I have a few questions about that: • Is this a/the recommended way to do this? • I can only find classes annotated with @JsExport in the generated Javascript/Typescript code, do I have to do that for everything? • I can’t annotate classes in the common/shared module of my Multiplatform library with @JsExport, how do I get those classes to show up in the generated JavaScript code?
s
Hi! • I believe that
binaries.executable()
is the one that produces .js + .d.ts • You have to use @JsExport for everything that you want to see in JS. If you care about bundle size, you might not want to overuse it though. • @JsExport is available in common code too. If you don't have an access to common/shared sources, you can create wrapper functions and classes and export those.
c
Hi, thanks for your response: • binaries.executable() also produces ,js + .d.ts files, however they are located inside build/js/node_modules/MyPackageName/kotlin ◦ that looks like a strange place to those files ◦ with binaries.executable() I get a folder “build/productionLibrary”, which sounds exactly like the thing I want. ▪︎ I found binaries.library() in some forum post, is there any official documentation somewhere? • I see, does this mean, that if I build my whole app around functions/classes from shared code, my App will get unreasonably large for a medium sized App? (Isn’t this kind of a dealbreaker?) • @JsExport is “available” in common code, meaning autocomplete suggests it to me. However the code does not compile, telling me that @JsExport is an unresolved reference. ◦ I was able to solve that by using
expect annotation class CommonJsExport()
in common module and
actual typealias CommonJsExport = JsExport
in Js module I ran into another problem: When I try to export a class that uses Ktor, my TypeScript file suddenly contains the following:
Copy code
type Nullable<T> = T | null | undefined
export namespace kotlinx.atomicfu {
    function atomic$ref$<T>(initial: T, trace: kotlinx.atomicfu.TraceBase): kotlinx.atomicfu.AtomicRef<T>;
    function atomic$boolean$(initial: boolean, trace: kotlinx.atomicfu.TraceBase): kotlinx.atomicfu.AtomicBoolean;
    function atomic$int$(initial: number, trace: kotlinx.atomicfu.TraceBase): kotlinx.atomicfu.AtomicInt;
    function atomic$long$(initial: kotlin.Long, trace: kotlinx.atomicfu.TraceBase): kotlinx.atomicfu.AtomicLong;
}
export namespace io.ktor.util {
    function AttributesJsFn(concurrent: boolean): io.ktor.util.Attributes;
}
This makes my angular app not compile, as it cannot find kotlinx.atomicfu.TraceBase or io.ktor.util.Attributes. If i delete these lines manually, the code works and I can do a network call from my angular app, using ktor from the common code (which is kinda cool 😃)
t
binaries.executable() also produces ,js + .d.ts files, however they are located inside build/js/node_modules/MyPackageName/kotlin
Distibution you can find in
build/distributions
folder of your subproject
a
If it is a library to import into something else like angular,
binaries.library()
is the way to go. We are currently using it with react-native. • I don't know why
@JsExport
is not resolved for you in common code. But it does work like a charm for us. • Regarding the
kotlinx.atomicfu
exports, I am also surprised. We also have them in our exports, but they do not hinder us from using our exported library. We use them with the exports defined in
.d.ts
without problems
s
… my App will get unreasonably large for a medium sized App?
I would expect code size to be reasonable. What I meant is that currently, if you JsExport declarations that you don’t end up using in JS, your bundle might be bigger than necessary.
However the code does not compile, telling me that @JsExport is an unresolved reference.
Could that be because you are not importing it? Could you try adding `
Copy code
import kotlin.js.JsExport
?
kotlin.js
package is not included by default in common code
c
@andylamax Thank you for your response, binaries.library() does in fact seem to be the thing that I am looking for. Regarding the
kotlinx.atomicfu
exports: My angular project does not compile, throwing the same error that my IDE is showing me (red underline in d..ts file):
TS2694: Namespace '".../jsLibraryForAngular/build/productionLibrary/jsLibraryForAngular".io.ktor.util' has no exported member 'Attributes'.
However, I am able to successfully compile and run the project by adding
"skipLibCheck": true
to my compiler options in my tsconfig.json file. This does seem like a hack though and feels like it’s going to end up causing problems down the road. The “faulty” lines are annotated even when looking at the .d.ts file from InteliJ, is it the same in your project? (What I am trying to find out is wether the error lies in my angular project configuration or in the d.ts file generation) @Svyatoslav Kuzmich [JB] manually importing the Annotation with
import kotlin.js.JsExport
did the trick, it now works. (The reason why I did not import it earlier is that it was not marked as an unresolved reference and even cmd-clicking it took me to the correct declaration file 🙃)
s
@Christian Gaisl Have you figure out a way to deal with
kotlinx.atomicfu.TraceBase
error for
typscript
? I mean, without using the
"skipLibCheck": true
@Svyatoslav Kuzmich [JB] Do you an answer for this? or is there a way to not have
atomicfu
in the
d.ts
generated file? The
TraceBase
reference is not there.
Atomicfu
comes from
coroutine
library as dependency
s
@shaktiman_droid I’m not sure what is happening with atomifu. Do you have an example of code that causes the problem?
s
@Svyatoslav Kuzmich [JB] So when you've either
coroutine
or
ktor
as dependency and you generate kotlin/js library. Your
d.ts
file contains following
Copy code
export namespace kotlinx.atomicfu {
    function atomic$ref$<T>(initial: T, trace: kotlinx.atomicfu.TraceBase): kotlinx.atomicfu.AtomicRef<T>;
    function atomic$boolean$(initial: boolean, trace: kotlinx.atomicfu.TraceBase): kotlinx.atomicfu.AtomicBoolean;
    function atomic$int$(initial: number, trace: kotlinx.atomicfu.TraceBase): kotlinx.atomicfu.AtomicInt;
    function atomic$long$(initial: kotlin.Long, trace: kotlinx.atomicfu.TraceBase): kotlinx.atomicfu.AtomicLong;
}
and it's just that. There is no reference generated for
kotlinx.atomicfu.TraceBase
and that causes compilation issue when
typescript
app is trying to use the above library. One needs to add
"skipLibCheck": true
so typescript doesn't strictly check for references and ignore that
TraceBase
is not found.
s
Thanks, reproduced it. Looks like a compiler bug 😞
s
@Svyatoslav Kuzmich [JB] Is there a way to solve it? If not, would you create a bug ticket to track it please?
We don't want our library user to add
"skipLibCheck": true
check because it's not ideal. Is there a temporary solution you can think of?
s
One workaround could be to add a fake
kotlinx.atomicfu.TraceBase
as a separate d.ts or concatenate it to existing one:
Copy code
declare namespace kotlinx.atomicfu { 
  interface TraceBase {}
}
👍 1
s
I tried something similar but it was erroring out. I didn't do exactly how you did it. So may be your example might work out. I did something like below,
Copy code
package kotlinx.atomicfu

@JsExport
external interface TraceBase
I'll follow your example and see what happens. I'll update tomorrow
s
s
thanks a ton @Svyatoslav Kuzmich [JB] 🙏
@Svyatoslav Kuzmich [JB] So I just realized that the snippet you shared with
export namespace
would be manual effort to add every time for a release. Is there a way to automatically include that in resulting
build folder
?
s
I think it is doable using gradle, with something like:
Copy code
val copyDtsPatch by task<Copy> {
    from("...")
    into("$buildDir/...")
}

val yourBuildTask by tasks.named("taskYouUseToBuildDistro") {
    dependsOn(copyDtsPatch)
}
But I’m not an expert, could be wrong.
👍 1
s
Will look into it. Not an expert in gradle as well but I'll try. Thanks @Svyatoslav Kuzmich [JB]
215 Views