Does anyone have (or is anyone working on) an efficient workflow for generating/maintaining `externa...
a
Does anyone have (or is anyone working on) an efficient workflow for generating/maintaining
external interface
s for `data class`es when working in a multiplatform project with JS targets? What about generating/maintaining the methods to map between the two? Context/Problem: We've written some `external interface`s for JS interop for methods that use simpler classes, but for complicated, nested, interconnected classes, it's a lot of effort to maintain this code. We're currently using
kotlinx.serialization
and
encodeToDynamic
/`encodeFromDynamic` to handle those complicated types. The developer experience within KMP when using
kotlinx.serialization
is much nicer, but: 1. we get
dynamic
types back instead of rich
external interface
s, so we don't get typescript definitions 2. serialization's json artifact is extremely heavyweight for this on web because it pulls in full JSON support for strings and JsonElement trees when we really just need lightweight methods that create and return plain JS objects as described here Potential Solution? It seems like those two problems above might be separately solvable? 1. Could be solved with an annotation processor to generate `external interface`s for classes annotated with
@Serializable
(or some other annotation) (which we can then cast the serialization-generated `dynamic`s to/from) 2. Can maybe be solved with an alternative serialization format that is a. only usable by the
js
target, b. operates directly on JS `Object`s, and c. excludes all the heavy stuff for string or JsonElements in the official
json
format If this sounds like stuff that anyone else is working on or running up against, I'd love to chat!
a
I am first hearing of encodeTo/From dynamic. However, I do agree with you. Serialization for JS is unreasonably heavy. Also, when it comes to maintaining external interfaces for data classes, it is a pain we are also currently suffering from. Sadly, we do not have any work around that just having to write the to & from methods. But this is a use case that would be better when addressed closely. This goes beyond just serialization. maybe treat all kotlin data classes as plain js objects. If not then, for every exported js object, a compiler plugin should also export an interface with to and from methods for conveniences. Now that I have written this down, I think I'll try to tackle this with some kind of compiler plugin in the near future
a
@andylamax I'd be more than happy to pair with you on that plugin! Serialization just for the JS/Kotlin interop is definitely overkill and heavy duty but it's the only way we've been able to avoid writing those mappers and interfaces. Some of the tricky cases might be custom mappings for enums, or deeply nested class hierarchies
g
I'm currently working on a KSP compiler in a multiplatform context including a JS export. We're trying to re-map some types like Array<->List and also generate classes for enums. For example the enum:
Copy code
@Export // My annotation
enum class MediaFileSource {
    SYNCHRO_CACHE,
    SMART_CACHE,
    REMOTE
}
will generate
Copy code
import com.[...].MediaFileSource as CommonMediaFileSource

@JsExport
public class MediaFileSource internal constructor(internal val `value`: CommonMediaFileSource) {
  public val name: String = value.name
}

public fun MediaFileSource.`import`() = value
public fun CommonMediaFileSource.export() = MediaFileSource(this)

@JsExport
public object MediaFileSources {
  public val SYNCHRO_CACHE: MediaFileSource = CommonMediaFileSource.SYNCHRO_CACHE.export()
  public val SMART_CACHE: MediaFileSource = CommonMediaFileSource.SMART_CACHE.export()
  public val REMOTE: MediaFileSource = CommonMediaFileSource.REMOTE.export()
}
So it looks like a bit better for our web team (they use typescript & js). I also work on classes and interfaces (PM if you want more examples, a bit verbose). No serialization here but it provides wrapper in both ways (JS <-> common Kotlin code), and since it's generated, the coupling is also generated (if a class contains a type that needs to be imported/exported, it will assume there will be a generated code for this class too, so that I don't care if my instance is complex or not, I just need to call import/export method). It's not yet open source but I thought it could maybe help you? (I mean at least some part could be copy/paste đŸ™‚)
đŸ˜® 1
đŸ‘€ 1