Home Web World JavaScript Compile your Kotlin Projects to JavaScript Modules

Compile your Kotlin Projects to JavaScript Modules

0
456
Kotlin Background
Kotlin Background

Kotlin allows the developers to compile the Kotlin project to Javascript modules for popular module systems.

The list of available options are:

  1. Plain (Default) – The compilation is not done for any module system. You can access the module by its name in the global system.
  2. Asynchronous Module Definition (AMD), which is in particular used by require.js library.
  3. CommonJS convention, widely used by node.js/npm (require function and module.exports object)
  4. Unified Module Definitions (UMD), which is compatible with both AMD and CommonJS, and works as “plain” when neither AMD nor CommonJS is available at runtime.

Choosing the Target Module System

Choosing the target module system depends on your build environment:

From IntelliJ IDEA

Setup per module: Open File -> Project Structure…, find your module in Modules and select “Kotlin” facet under it. Choose appropriate module system in “Module kind” field.

Setup for the whole project: Open File -> Settings, select “Build, Execution, Deployment” -> “Compiler” -> “Kotlin compiler”. Choose appropriate module system in “Module kind” field.

From Maven

To select module system when compiling via Maven, you should set moduleKind configuration property, i.e. your pom.xml should look like this:

<plugin>
    <artifactId>kotlin-maven-plugin</artifactId>
    <groupId>org.jetbrains.kotlin</groupId>
    <version>${kotlin.version}</version>
    <executions>
        <execution>
            <id>compile</id>
            <goals>
                <goal>js</goal>
            </goals>
        </execution>
    </executions>
    <!-- Insert these lines -->
    <configuration>
        <moduleKind>commonjs</moduleKind>
    </configuration>
    <!-- end of inserted text -->
</plugin>

Available values are: plainamdcommonjsumd.

From Gradle

To select module system when compiling via Gradle, you should set moduleKind property, i.e.

compileKotlin2Js.kotlinOptions.moduleKind = "commonjs"

Available values are similar to Maven.

@JsModule annotation

To tell Kotlin that an external class, package, function or property is a JavaScript module, you can use @JsModule annotation. Consider you have the following CommonJS module called “hello”:

module.exports.sayHello = function(name) { alert("Hello, " + name); }

 

You should declare it like this in Kotlin:

@JsModule("hello")
external fun sayHello(name: String)

Applying @JsModule to packages

Some JavaScript libraries export packages (namespaces) instead of functions and classes. In terms of JavaScript it’s an object that has members that are classes, functions and properties. Importing these packages as Kotlin objects often looks unnatural. The compiler allows to map imported JavaScript packages to Kotlin packages, using the following notation:

@file:JsModule("extModule")
package ext.jspackage.name

external fun foo()

external class C

where the corresponding JavaScript module is declared like this:

module.exports = {
    foo:  { /* some code here */ },
    C:  { /* some code here */ }
}

Important: files marked with @file:JsModule annotation can’t declare non-external members. The example below produces compile-time error:

@file:JsModule("extModule")
package ext.jspackage.name

external fun foo()

fun bar() = "!" + foo() + "!" // error here

Importing deeper package hierarchies

In the previous example the JavaScript module exports a single package. However, some JavaScript libraries export multiple packages from within a module. This case is also supported by Kotlin, though you have to declare a new .kt file for each package you import.

For example, let’s make our example a bit more complicated:

module.exports = {
    mylib: {
        pkg1: {
            foo: function() { /* some code here */ },
            bar: function() { /* some code here */ }
        },
        pkg2: {
            baz: function() { /* some code here */ }
        }
    }
}

To import this module in Kotlin, you have to write two Kotlin source files:

@file:JsModule("extModule")
@file:JsQualifier("mylib.pkg1")
package extlib.pkg1

external fun foo()

external fun bar()

and

@file:JsModule("extModule")
@file:JsQualifier("mylib.pkg2")
package extlib.pkg2

external fun baz()

@JsNonModule annotation

When a declaration has @JsModule, you can’t use it from Kotlin code when you don’t compile it to a JavaScript module. Usually, developers distribute their libraries both as JavaScript modules and downloadable .js files that you can copy to project’s static resources and include via HTML Script tag.

element. To tell Kotlin that it’s ok to use a @JsModule declaration from non-module environment, you should put @JsNonModule declaration. For example, given JavaScript code:

function topLevelSayHello(name) { alert("Hello, " + name); }
if (module && module.exports) {
    module.exports = topLevelSayHello;
}

can be described like this:

@JsModule("hello")
@JsNonModule
@JsName("topLevelSayHello")
external fun sayHello(name: String)