The plugin is published in the Gradle plugin registry, and the source code is published on GitHub.
An example project that illustrates the use of the plugin is also provided on GitHub.
The plugin covers two major issues that occur when trying to integrate Kotlin and TypeScript code.
The plugin is designed for use alongside the Kotlin multiplatform plugin. In order to generate the TypeScript declarations, the plugin makes use of the modules generated for a JVM target.
The plugin is added in the usual way as shown below:
plugins { id("net.akehurst.kotlin.kt2ts") version("1.4.0") }
It can be configured using the kt2ts DSL extension, as described below.
To add TypeScript declarations and a package.json to your own modules, add the following:
kt2ts { classPatterns.set(listOf( "com.example.my.module.common.*" )) }
This will create two tasks:
and it will cause the jsJar task to have a dependency on them.
These tasks generate a TypeScript declaration file that contains declarations for everything contained in the com.example.my.module.common package. You can also specify classes individually if you do not want to generate TypeScript for everything in a package.
The resulting jsJar file that is built will contain the TypeScript declarations file, a package.json file, and the JavaScript that is generated by Kotlin.
A minimal package.json file is generated, like the following:
{ "name": "com.example-my-module-common" "version": "1.0.0-SNAPSHOT", "main": "./com.example-my-module-common.js", "types": "./com.example-my-module-common.d.ts" }
This section describes the integration with an Angular build. However, one could potentially use the plugin with any TypeScript-based frontend.
You use the plugin to assist with building your Node.js-based frontend. To do this, it is necessary to define the location of the Angular source code directory.
kt2ts { nodeSrcDirectory.set( project.layout.projectDirectory.dir("src/angular") ) }
If you set the nodeSrcDirectory property then a number of extra tasks are added to the build. These new tasks do the following things:
yarn install
, which downloads and populates the node_modules directory for the Angular code.ng build
, which builds the Angular code.The tasks are all appropriately linked with dependencies. You need only to execute the following command:
gradle build
Of course, we do not want to build the Angular application in isolation. We want to add Kotlin modules that are to be used in the Angular application.
The plugin adds a dependency configuration, named nodeKotlin by default, that should be used to add dependencies to Kotlin multiplatform modules.
For example, assuming you have:
Then we can add the module to the Angular build using the nodeKotlin dependency configuration:
dependencies { nodeKotlin("com.example:my-module-common:1.0.0") }
This module will be unpacked in the Angular node_modules directory and can then be used in the Angular-TypeScript code as follows:
import * as mmc_js from 'com.example-my-module-common'; import mmc = mmc_js.com.example.my.module.common; ... let x = new mmc.MyCommonClass();
The second import is not essential, it simply provides an alias to the content of the imported package. The Kotlin-generated JavaScript is namespaced according to the original Kotlin packages.
There are a number of additional configuration options that add extra features and assistance.
Often you want to include, into your Node.js based build, third-party modules for which there is no TypeScript declaration file. For example, org.jetbrains.kotlinx:kotlinx-coroutines-core or com.soywiz.korlibs.klock:klock (a very useful Kotlin multiplatform date-time library).
For these libraries, we do not have control of the source, so we cannot generate the TypeScript declarations as part of the {module}-js.jar. However, we can generate the declarations as part of the “unpacking” process, i.e., we can generate the declarations after the unpackKotlinJs task.
There is another configuration option generateThirdPartyModules that is part of the plugin’s Gradle configuration DSL. This can be used to configure TypeScript declaration generation for modules after they are unpacked.
kt2ts { ... generateThirdPartyModules { register("${group_klock}:klock:${version_klock}") { includeOnly.set(listOf("com.soywiz.korlibs.klock:klock-jvm")) moduleGroup.set("klock-root") moduleName.set("klock") tgtName.set("klock-root-klock") classPatterns.set(listOf( "com.soywiz.klock.DateTime", "com.soywiz.klock.DayOfWeek", "com.soywiz.klock.DateTimeTz", "com.soywiz.klock.TimezoneOffset", "com.soywiz.klock.Month", "com.soywiz.klock.Time", "com.soywiz.klock.Year", "com.soywiz.klock.YearMonth", "com.soywiz.klock.TimeSpan", "com.soywiz.klock.DateTimeSpan", "com.soywiz.klock.MonthSpan", "com.soywiz.klock.DateFormat" )) } } }
Sometimes we have the requirement to access Kotlin Javascript classes via reflection. Unfortunately, most of the Javascript support for Kotlin reflection is not yet implemented. There is a small Kotlin multiplatform library that can be used to augment Kotlin reflection from Javascript. It is not complete either, but it adds a few things that are currently unavailable in the Kotlin standard reflection library.
This is used, for example, as part of a reflection-based JSON serialisation library, see the example project for an indication of how it is used.
In order to use the reflection library, it is necessary to “register” (in Kotlin code) the Kotlin modules that you wish to reflect over, i.e.,
import net.akehurst.kotlinx.reflect.ModuleRegistry ... ModuleRegistry.register("com.example:my-module-common")
Correspondingly, in order to make these modules available in a “webpacked” Node.js application, it is necessary to generate some special code as part of a dynamic require function.
There is a specific configuration option, dynamicImport, for doing this as part of the plugin’s Gradle configuration DSL, e.g.,
kt2ts { ... dynamicImport.set(listOf( "com.example:my-module-common" )) }
mapOf( "kotlin.reflect.KClass" to "any", "kotlin.Unit" to "void", "kotlin.Any" to "any", "kotlin.Array" to "Array", "kotlin.CharSequence" to "string", "kotlin.Char" to "number", "kotlin.String" to "string", "kotlin.Number" to "number", "kotlin.Byte" to "number", "kotlin.Short" to "number", "kotlin.Int" to "number", "kotlin.Float" to "number", "kotlin.Double" to "number", "kotlin.Boolean" to "boolean", "kotlin.Throwable" to "Error", "kotlin.Exception" to "Error", "kotlin.RuntimeException" to "Error", "java.lang.Exception" to "Error", "java.lang.RuntimeException" to "Error" )
mapOf( "org.jetbrains.kotlinx:kotlinx-coroutines-core-js" to "kotlinx-coroutines-core", "org.jetbrains.kotlinx:kotlinx-coroutines-core" to "kotlinx-coroutines-core", "org.jetbrains.kotlinx:kotlinx-coroutines-core-common" to "kotlinx-coroutines-core-common", "org.jetbrains.kotlinx:kotlinx-coroutines-io-js" to "kotlinx-io-kotlinx-coroutines-io", "org.jetbrains.kotlinx:kotlinx-coroutines-io" to "kotlinx-io-kotlinx-coroutines-io", "org.jetbrains.kotlinx:kotlinx-io-js" to "kotlinx-io", "org.jetbrains.kotlinx:kotlinx-io" to "kotlinx-io", "org.jetbrains.kotlinx:atomicfu-common" to "kotlinx-atomicfu", "org.jetbrains.kotlinx:atomicfu-js" to "kotlinx-atomicfu", "org.jetbrains.kotlinx:atomicfu" to "kotlinx-atomicfu", "io.ktor:ktor-http-cio-js" to "ktor-ktor-http-cio", "io.ktor:ktor-http-cio" to "ktor-ktor-http-cio", "io.ktor:ktor-client-core-js" to "ktor-ktor-client-core", "io.ktor:ktor-client-core" to "ktor-ktor-client-core", "io.ktor:ktor-client-websockets-js" to "ktor-ktor-client-websockets", "io.ktor:ktor-client-websockets" to "ktor-ktor-client-websockets", "io.ktor:ktor-http-js" to "ktor-ktor-http", "io.ktor:ktor-http" to "ktor-ktor-http", "io.ktor:ktor-utils-js" to "ktor-ktor-utils", "io.ktor:ktor-utils" to "ktor-ktor-utils" )
Kotlin
package com.example.my.module.common data class MyCommonClass(name: String) { var list = mutableListOf<Pair<Int,Boolean>>() }
Typescript Declaration
import * as $kotlin from 'kotlin'; declare namespace com.example.my.module.common { class MyCommonClass { constructor(name: string); list: $kotlin.collections.List<$kotlin.Pair<number,boolean>> } }
itemis AG
Am Brambusch 15
Lünen NRW 44536
+49 231 9860-606
info@itemis.com