If you have something that is documented to be use...
# javascript
v
If you have something that is documented to be used like this from node.js code:
Copy code
require('@vercel/ncc')(input, {
    sourceMap: true
})
How do you properly formulate this as externals definition so that it can be used in Kotlin code?
t
Copy code
@JsModule("@vercel/ncc")
external fun ncc(input: Any, options: Any)

// call
val options = jsObject {
  sourceMap = true
};

ncc(input, options)
For some modules additional
default
export manipulation required
v
Nice, thanks, will try it
Works fine, except for one detail, hopefully you have another hint for me. One of the fields if sent through
console.log(result.assets)
is printed as
Copy code
[Object: null prototype] {
  LICENSES: {
    source: <Buffer 40 61 63 74 69 6f 6e 73 2f 63 61 63 68 65 0a 4d 49 54 0a 0a 40 61 63 74 69 6f 6e 73 2f 63 6f 72 65 0a 4d 49 54 0a 0a 40 61 63 74 69 6f 6e 73 2f 65 78 ... 60003 more bytes>,
    permissions: undefined
  },
  'sourcemap-register.js': {
    source: <Buffer 6d 6f 64 75 6c 65 2e 65 78 70 6f 72 74 73 20 3d 0a 2f 2a 2a 2a 2a 2a 2a 2f 20 28 66 75 6e 63 74 69 6f 6e 28 6d 6f 64 75 6c 65 73 2c 20 72 75 6e 74 69 ... 124166 more bytes>,
    permissions: 438
  }
}
How do I declare this properly to be able to iterate over the entries? I tried with
Copy code
external interface NccResult {
    val code: String
    val map: String?
    val assets: Map<String, Asset>?
}

external interface Asset {
    val source: Buffer
    val permissions: Number?
}
and then
Copy code
result.assets?.forEach { (assetFileName, asset) -> /* ... */ }
But that complains with
Cannot read property 'iterator' of undefined
p
AFAIK If the properties of
result.assets
are not known at compile time you will have to parse them with a proper parser to get a map. kotlinx.serialisation has some nice functions to parse from dynamic to proper kotlin data structures. Below is the hacky version, it works as long as you are aware that everything behaves as if it were an external interface (no toString, equals etc.)
Copy code
js("Object").keys(result.assets).unsafeCast<Array<dynamic>>.forEach(){ key ->
print("$key=${results.assets.asDynamic()[key]}"
}
v
Yeah, that's roughly how I do it right now:
Copy code
external interface NccResult {
    val code: String
    val map: String?
    val assets: dynamic
}

// ...

val assets = result.assets
if (assets != null) {
    for (assetFileName in js("Object").keys(assets).unsafeCast<Array<String>>()) {
        val asset = assets[assetFileName].unsafeCast<Asset>()
        // ...
    }
}
But I hoped to be able to improve my externals definition to have a more Kotlin way
Well, my workaround is a little bit nicer now as I have
kotlin-extensions
from
kotlin-wrappers
present anyway:
Copy code
external interface NccResult {
    val code: String
    val map: String?
    val assets: dynamic
}

// ...

if (result.assets != null) {
    for (assetFileName in Object.keys(result.assets)) {
        val asset = result.assets[assetFileName].unsafeCast<Asset>()
        // ...
    }
}
But it would still be nice to be able to declare (and use)
assets
as
Map<String, Asset>?
somehow.
Hm, this is what seems to be missing: https://youtrack.jetbrains.com/issue/KT-5654
Ah, now I have it :-)
Copy code
external interface NccResult {
    val code: String
    val map: String?
    val assets: AssetMap?
}

external interface AssetMap
inline operator fun AssetMap.get(key: String) = asDynamic()[key].unsafeCast<Asset>()
inline operator fun AssetMap.set(key: String, value: Asset) {
    asDynamic()[key] = value
}
inline operator fun AssetMap.iterator() = Object.keys(this).map { it to this[it] }.iterator()
inline fun AssetMap.forEach(action: (Pair<String, Asset>) -> Unit) {
    for (element in this) {
        action(element)
    }
}

// ...

result.assets?.forEach { (assetFileName, asset) ->
    // ...
}
👏 1