Hi, I try to use a npm package in a mpp project. ...
# javascript
p
Hi, I try to use a npm package in a mpp project. Like from the example: dependencies {     // ...     implementation(npm("is-sorted", "1.0.5")) } @JsModule("is-sorted") @JsNonModule external fun <T> sorted(a: Array<T>): Boolean console.log("Hello, Kotlin/JS!") console.log(sorted(arrayOf(1,2,3))) console.log(sorted(arrayOf(3,1,2))) Now I wanna use another npm library with the name kafkjs in that way: dependencies {     // ...    implementation (npm( "kafkajs", "^1.12.0")) } fun createConsumer(): dynamic { return js( “kafka.consumer({groupID: ‘test-group’ })” ) } but I alwas get the error: ReferenceError: kafka is not defined Any ideas what I am doing wrong?
t
Main problem - no kafka declarations
Copy code
// declarations
@JsModule("kafka")
external object kafka {
    fun consumer(options: ConsumerOptions): dynamic
}

external interface ConsumerOptions {
    var groupID: String
}

// code
fun createConsumer(): dynamic {
val options: ConsumerOptions = js("({})")
options.groupID = "test-group"
return kafka.consumer(options)
}
Declarations required for “autoimport”
p
Thank you for the help. Now I get another error: Cannot find module 'kafka' When I am right, it comes that the module should be kafkajs. In JS I use this with: const { Kafka } = require('kafkajs') When I change that it comes to a TypeError: $module$kafkajs.consumer is not a function But I don`t understand why, at the moment.
i
In fact you require not
consumer
but
Kafka
So in fact in external declaration you have
Copy code
@file:JsModule(“kafkajs”) // file means that all further external declarations will be found as a subobject on “kafkajs”

external object Kafka {
   fun consumer(...)
}
p
That means this should run:
Copy code
@JsModule("kafkajs")
@JsNonModule
external object Kafka {
    fun consumer(options: ConsumerOptions): dynamic
}

external interface ConsumerOptions {
    var groupID: String
}

fun createConsumer(): dynamic {
    val options: ConsumerOptions = js("({})")
    options.groupID = "test-group"

    return Kafka.consumer(options)
}
But with this I get the TypeError. TypeError: $module$kafkajs.consumer is not a function
i
In your decision, you mark external object with
@JsModule
. It means that all your external object looked as a JsModule. It is similar with JS code
Copy code
const { consumer } = require("kafkajs")
But you need if I understand correctly
Copy code
const { Kafka } = require("kafkajs")
const consumer = Kafka.consumer
For this you can mark all file with
@JsModule
telling that all external declarations are part of JsModule
Copy code
@file:JsModule("kafkajs") // file: is important!

external object Kafka {
    fun consumer(options: ConsumerOptions): dynamic
}
external interface ConsumerOptions {
    var groupID: String
}
p
Ok now im lost. When I mark the file with
@JsModule
how can I pass the configuration options to the consumer? I mean the original function is not usable any more.
Copy code
fun createConsumer(): dynamic {
    val options: ConsumerOptions = js("({})")
    options.groupID = "test-group"

    return Kafka.consumer(options)
}
And how should my call from NodeJS look like? Please excuse the many questions I am completely new in Kotlin / JS and I want to learn and understand it.
i
Yes, for now if you mark file with
@JsModule
it means that you can add only external declarations to this file, you can move non-external fun to another file. In all other questions it should be completely the same
t
@Patrick Doering Also you can vote here - https://youtrack.jetbrains.com/issue/KT-37776 🙂
👍 1
p
@Ilya Goncharov [JB] as I see it right in the documentation I need following:
Copy code
const kafka = new Kafka({
  clientId: 'my-app',
  brokers: ['kafka1:9092', 'kafka2:9092']
})
 
const producer = kafka.producer()
const consumer = kafka.consumer({ groupId: 'test-group' })
If I understand you right I have to implement it in that way: File: KafkaImpl.kt
Copy code
@file:JsModule("kafkajs") // file: is important!

package <myCoolPackage>

external object Kafka {
    fun producer(): dynamic
    fun consumer(options: ConsumerOptions): dynamic
}
external interface ConsumerOptions {
    var groupID: String
}
and another File: KafkaInitialisation.kt
Copy code
fun createConsumer(): dynamic {
    val options: ConsumerOptions = js("({})")
    options.groupID = "test-group"
    return Kafka.consumer(options)
}

fun createProducer(): dynamic {
    return Kafka.producer()
}
And call them from node with:
Copy code
const myKafka = require('../build/js/packages/kafkaDemo').de.kafkaDemo.kafka;
async function testCall() {
  const producer = myKafka.createProducer();
  const consumer = myKafka.createConsumer();
}

testCall();
Is that right?
i
Yes, it is right If you are interesting in integration with external JS project, you can check it, maybe it will be useful https://github.com/ilgonmic/kotlin-ts
p
Thank you. So I implemented in that way and it is actual not running. I always get the error. UnhandledPromiseRejectionWarning: TypeError: Kafka.producer is not a function I will check your repo. And hopefully find a solution.
I don`t see my error. Everytime when I try it is not working. https://github.com/munichbughunter/callJSLib
i
As I can see, kafkajs initially requires to create Kafka instance, and only after that to call
producer
and
consumer
on instance. So it means that in your external declarations
Kafka
should be class But I can recommend to use
dukat
, you can send third parameter into
npm
dependency
Copy code
implementation(npm("kafkajs", "...", true))
and it include generated external declarations into your build. There is small trick, because
kafkajs
uses
@types/nodejs
, dukat can’t generate it, but we have nodejs declarations here (https://github.com/Kotlin/kotlinx-nodejs), you can have dependency on it
And it requires to make changes in your
KafkaInitialisation
something like this
Copy code
fun createConsumer(): dynamic {
    val options: KafkaConfig = js("({})")
    val consumerOptions: ConsumerConfig = js("({})")
    consumerOptions.groupId = "test-group"
    return Kafka(options).consumer(consumerOptions)
}

fun createProducer(): dynamic {
    val options: KafkaConfig = js("({})")
    return Kafka(options).producer()
}
But honestly, now I see that
dukat
generates a bit errors (it does not provide
@JsModule
annotation in this case cc @[JB] Shagen) So you can use manual task
*generateExternals
and find declarations in folder
externals
and copy it inside your sources, and make all necessary changes
p
I will evaluate or later. I have kiddies Time. 😅
i
Seems that something work here (https://github.com/ilgonmic/callJSLib) - Kafka error, but it seems that it is kafka related specific I can reach it with next steps 1. Add
kotlinx-nodejs
dependency 2. Run task
nodeJsGenerateExternals
3. Copy from
externals
to sources 4. Add to
index.module_kafkajs.kt
on top
@file:JsModule("kafkajs")
5. Move all typealiases (non external) to other file (in my case
index.module_kafkajs2.kt
😎 1
p
Wohoo @Ilya Goncharov [JB] it works now. Really really great. Thanks for all the help in my journey of learning Kotlin/JS (and particularly using it in nodeJS). Great learning experience for me.