JavaScript Modules

You can compile your Kotlin projects to JavaScript modules for various popular module systems. We currently support the following configurations for JavaScript modules:

  • Unified Module Definitions (UMD), which is compatible with both AMD and CommonJS. UMD modules are also able to be executed without being imported or when no module system is present. This is the default option for the browser and nodejs targets.
  • Asynchronous Module Definitions (AMD), which is in particular used by the RequireJS library.
  • CommonJS, widely used by Node.js/npm (require function and module.exports object)
  • Plain. Don’t compile for any module system. You can access a module by its name in the global scope.

Targeting the browser

If you’re targeting the browser and want to use a different module system than UMD, you can specify the desired module type in the webpackTask configuration block. For example, to switch to CommonJS, use:

  1. kotlin {
  2. js {
  3. browser {
  4. webpackTask {
  5. output.libraryTarget = "commonjs2"
  6. }
  7. }
  8. binaries.executable()
  9. }
  10. }

Webpack provides two different “flavors” of CommonJS, commonjs and commonjs2, which affect the way your declarations are made available. While in most cases, you probably want commonjs2, which adds the module.exports syntax to the generated library, you can also opt for the “pure” commonjs option, which implements the CommonJS specification exactly. To learn more about the difference between commonjs and commonjs2, check here.

Creating JavaScript libraries and Node.js files

If you are creating a library that will be consumed from JavaScript or a Node.js file, and want to use a different module system, the instructions are slightly different.

Choosing the target module system

To select module kind, set the moduleKind compiler option in the Gradle build script.

  1. compileKotlinJs.kotlinOptions.moduleKind = "commonjs"
  1. tasks.named<KotlinJsCompile>("compileKotlinJs").configure {
  2. kotlinOptions.moduleKind = "commonjs"
  3. }

Available values are: umd (default), commonjs, amd, plain.

This is different from adjusting webpackTask.output.libraryTarget. The library target changes the output generated by webpack (after your code has already been compiled). kotlinOptions.moduleKind changes the output generated by the Kotlin compiler.

In the Kotlin Gradle DSL, there is also a shortcut for setting the CommonJS module kind:

  1. kotlin {
  2. js {
  3. useCommonJs()
  4. // . . .
  5. }
  6. }

@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”:

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

You should declare it like this in Kotlin:

  1. @JsModule("hello")
  2. 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 can map imported JavaScript packages to Kotlin packages, using the following notation:

  1. @file:JsModule("extModule")
  2. package ext.jspackage.name
  3. external fun foo()
  4. external class C

where the corresponding JavaScript module is declared like this:

  1. module.exports = {
  2. foo: { /* some code here */ },
  3. C: { /* some code here */ }
  4. }

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

  1. @file:JsModule("extModule")
  2. package ext.jspackage.name
  3. external fun foo()
  4. 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:

  1. module.exports = {
  2. mylib: {
  3. pkg1: {
  4. foo: function() { /* some code here */ },
  5. bar: function() { /* some code here */ }
  6. },
  7. pkg2: {
  8. baz: function() { /* some code here */ }
  9. }
  10. }
  11. }

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

  1. @file:JsModule("extModule")
  2. @file:JsQualifier("mylib.pkg1")
  3. package extlib.pkg1
  4. external fun foo()
  5. external fun bar()

and

  1. @file:JsModule("extModule")
  2. @file:JsQualifier("mylib.pkg2")
  3. package extlib.pkg2
  4. external fun baz()

@JsNonModule annotation

When a declaration is marked as @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 your project’s static resources and include via a <script> tag. To tell Kotlin that it’s okay to use a @JsModule declaration from a non-module environment, add the @JsNonModule annotation. For example, consider the following JavaScript code:

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

You could describe it from Kotlin as follows:

  1. @JsModule("hello")
  2. @JsNonModule
  3. @JsName("topLevelSayHello")
  4. external fun sayHello(name: String)

Module system used by the Kotlin Standard Library

Kotlin is distributed with the Kotlin/JS standard library as a single file, which is itself compiled as an UMD module, so you can use it with any module system described above. While for most use cases of Kotlin/JS, it is recommended to use a Gradle dependency on kotlin-stdlib-js, it is also available on NPM as the kotlin package.