npm link packages generated by KotlinJS

Viewed 399

I've created a Kotlin Multiplatform application that generates a JS and TypeScript output file, i'm trying to make use of those output files inside a TypeScript project I'm fairly new to TypeScript as well as the whole npm ecosystem

enter image description here

The first package.json inside the hotel directory looks like this:

{
  "main": "kotlin/hotel.js",
  "devDependencies": {
    "webpack": "4.42.1",
    "webpack-cli": "3.3.11",
    "source-map-loader": "0.2.4",
    "webpack-dev-server": "3.10.3",
    "css-loader": "3.4.2",
    "style-loader": "1.1.3"
  },
  "dependencies": {
    "kotlin": "file:/home/vlad/Code/hotel-state/build/js/packages_imported/kotlin/1.4.0-M2",
    "kotlin-source-map-loader": "file:/home/vlad/Code/hotel-state/build/js/packages_imported/kotlin-source-map-loader/1.4.0-M2"
  },
  "peerDependencies": {},
  "optionalDependencies": {},
  "bundledDependencies": [],
  "name": "hotel",
  "version": "1.0.0"
}

The second package.json inside the js directory looks like this:

{
  "private": true,
  "workspaces": [
    "packages/hotel",
    "packages/hotel-test"
  ],
  "devDependencies": {},
  "dependencies": {},
  "peerDependencies": {},
  "optionalDependencies": {},
  "bundledDependencies": [],
  "name": "hotel",
  "version": "1.0.0"
}

From the hotel directory, i've done

sudo npm link hotel 

In my second project, i now add this to the package.json followed by an npm install

  "dependencies": {
    "hotel": "file:/usr/lib/node_modules/hotel",
    "lit-html": "^1.2.1"
  },

enter image description here

When i hit autocomplete, i can see the hotel package, but it keeps on showing a red line underneath it. When i try to use it in my other TypeScript files, it stops compiling

The typescript headers in that hotel.d.ts has a namespace

declare namespace hotel {
    type Nullable<T> = T | null | undefined
    namespace com.harakati.hotel {
        /* ErrorDeclaration: Class com.harakati.hotel.TH with kind: OBJECT */
    }

    namespace com.harakati.hotel {
        class Room {
            constructor(roomNumber: Nullable<number>)
            static Room_init_$Create$(roomNumber: Nullable<number>, $mask0: number, $marker: Nullable<any /*Class kotlin.js.DefaultConstructorMarker with kind: OBJECT*/>): com.harakati.hotel.Room
            roomNumber: number;
        }
    }

Am i doing the linking correctly?

Am i including this linked package correctly?

Is there anythin else that i might be missing?

1 Answers

Turns out the generated TypeScript headers in Kotlin 1.4 M2 aren't being exported and somebody has already logged a bug for it

https://youtrack.jetbrains.com/issue/KT-37883

A quick and dirty workaround i wrote is to run some Kotlin script at the end of a Gradle build to post-process the generated TypeScript.

Include this inside the Kotlin block in your build.gradle.kts and the generated TS headers will import without problems in the TS project.

    gradle.buildFinished {
        println("==============================================================")
        val path = "$buildDir/js/packages/${project.name}/kotlin/${project.name}.d.ts"
        println("Making some adjustments to \n$path")
        val newLines = mutableListOf<String>()
        println("==============================================================")
        if (File(path).exists()) {
            File(path).readLines().forEach { line ->
                if (line.startsWith("declare namespace") || line.startsWith("}")) {
                    "// $line".let {
                        newLines.add(it)
                        println(it)
                    }
                } else if (line.trim().startsWith("namespace")) {
                    line.replace("namespace", "export namespace").let {
                        newLines.add(it)
                        println(it)
                    }
                } else {
                    line.let {
                        newLines.add(it)
                        println(it)
                    }
                }
            }
            File(path).writeText(newLines.joinToString("\n"))
        } else {
            println("$path does not exist")
        }
        println("==============================================================")
    }

Inside package.json in your TypeScript project, include it becomes straight forward

  "dependencies": {
    "hotel": "/home/vlad/Code/.../hotel/build/js",
    "lit-html": "^1.2.1",
    "tslib": "^2.0.0"
  },

or simply use npm link to create a simlink to the generated output which then allows you to point to point to the link instead of the absolute path which could change if you move stuff around.

Related