karakum-team / karakum

Converter of TypeScript declaration files to Kotlin declarations
Apache License 2.0
35 stars 0 forks source link

Empty generated file for `@blueprintjs/icons` #9

Open joffrey-bion opened 1 year ago

joffrey-bion commented 1 year ago

I'm trying to generate typings for @blueprintjs/icons (I'm starting with this simple one as a PoC, and then I intend to move on to more complicated modules of course). See https://github.com/joffrey-bion/kotlin-blueprintjs for my existing declarations.

In this @blueprintjs/icons NPM module, the package.json's typings property points to this lib/esm/index.d.ts definition file:

export { IconSvgPaths16, IconSvgPaths20, iconNameToPathsRecordKey } from "./iconSvgPaths";
export { getIconContentString, IconCodepoints } from "./iconCodepoints";
export { IconName, IconNames } from "./iconNames";

But when I generate external from it using Karakum, I only get this empty-ish index.kt file:

@file:JsModule("blueprintjs-icons/index")
@file:Suppress(
    "NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE",
)

package blueprintjs.icons

/* export { IconSvgPaths16, IconSvgPaths20, iconNameToPathsRecordKey } from "./iconSvgPaths"; */

/* export { getIconContentString, IconCodepoints } from "./iconCodepoints"; */

/* export { IconName, IconNames } from "./iconNames"; */

Here is my config for the Karakum Gradle plugin (in case it's useful):

{
    "input": "<nodeModules>/@blueprintjs/icons/lib/esm/index.d.ts",
    "output": "src/jsMain/generated",
    "libraryName": "blueprintjs-icons"
}

So I have a couple questions:

  1. Why is the JsModule called blueprintjs-icons/index? Is this because it's <libraryName>/<originalFileName>? Could this cause a problem when importing these declarations in Kotlin? Or will this work out of the box?

  2. Why is everything commented?

sgrishchenko commented 1 year ago

Why is the JsModule called blueprintjs-icons/index? Is this because it's /? Could this cause a problem when importing these declarations in Kotlin? Or will this work out of the box?

By default Karakum tries to preserve original structure of files, it may be good for granular imports (if library contains 100 files, but you need just one of them), but maybe not good strategy it is for small libs, so you can override it using moduleNameMapper:

    "moduleNameMapper": {
        ".*": "blueprintjs-icons"
    }

Why is everything commented?

Because there is no analog for re-export in Kotlin syntax :slightly_smiling_face: It is how Karakum works: if it meet something that it does not know how to convert, it just leaves the original code in comments.

joffrey-bion commented 1 year ago

By default Karakum tries to preserve original structure of files

I'm sorry my knowledge of Kotlin externals is a bit limited, but I didn't think the file structure had anything to do with the module names. I thought it was independent. I'm totally ok with a file named index.kt matching the original index.ts. I'm just surprised about the module name, I thought all files exporting top-level declarations from the blueprintjs-icons JS module should also have a JsModule with the name of the original JS module.

It is how Karakum works: if it meet something that it does not know how to convert, it just leaves the original code in comments

But what can I do to make things work then? I mean the other files aren't converted as they should. I don't mind that it doesn't re-export from the index file, but Karakum should follow the import and convert the corresponding files too, right?

Here I would expect ./iconSvgPaths.d.ts, ./iconCodepoints.d.ts, and ./iconNames.d.ts to be converted to the corresponding Kotlin externals, in separate files, all exporting their corresponding top-level declarations under the same module name blueprintjs-icons (and under the same kotlin package name blueprintjs.icons

sgrishchenko commented 1 year ago

Now I see that my initial advice was incorrect, you should specify @blueprintjs/icons as libraryName in your config file, because it is actually name of NPM package and then use this mapping

    "moduleNameMapper": {
        ".*": "@blueprintjs/icons"
    }

If you want to see @file:JsModule("@blueprintjs/icons") instead of @file:JsModule("@blueprintjs/icons/index") in you generated files.

I thought all files exporting top-level declarations from the blueprintjs-icons JS module should also have a JsModule with the name of the original JS module.

And they do. The thing it that there is no blueprintjs-icons (or to be correct @blueprintjs/icons) JS module in reality, JS module is file with JS code + js extension. @blueprintjs/icons is package, package is a folder that marked by package.json file. So Node module resolution works this way: if there is main or exports field in package.json, then it forwards import from package name to file that specified main or exports field (e.g. if you have ./lib/esm/index.js in you package.json and your package is @blueprintjs/icons, so import "@blueprintjs/icons" will be resolved to import "@blueprintjs/icons/lib/esm/index.js"). So, actually Karakum tries to write into @JsModule name of JS file, TS declarations were written for.

Here I would expect ./iconSvgPaths.d.ts, ./iconCodepoints.d.ts, and ./iconNames.d.ts to be converted to the corresponding Kotlin externals

It is fair expectation, but what if @blueprintjs/icons depends on third party library (and I believe it does)? Should Karakum convert files in that libraries too? Now the strategy of conversion is more strict: only files, that specified in input field will be converted. If you have multiple files in your library you can use glob expressions or even array of glob expressions.