https://kotlinlang.org logo
#javascript
Title
# javascript
e

Edoardo Luppi

11/24/2023, 4:57 PM
Can I use external libraries which use the ES6 module system?
a

Artem Kobzar

11/24/2023, 5:38 PM
If you use ES-modules in your project - yes. To enable it you should call
useEsModules
inside your
js
block inside the
build.gradle
file
In nodejs it's also possible with commonjs
So, if you use NodeJS and commonjs module system - the answer also yes
Or webpack
It also could work with the mixed module systems
e

Edoardo Luppi

11/24/2023, 5:43 PM
Thanks! So what I'm doing is: 1. generate a series of JS files (use ES6) 2. copy a dummy
package.json
into the output folder At this point the result package layout is as per screenshot. Each file has a
export default class
3. at this point, how am I supposed to import those classes into my Kotlin module using externals?
And to import that package as a dependency, I'm using
Copy code
jsMain {
  dependencies {
    implementation(npm("antlr4", "4.13.1-patch-1"))

    val pathToLocalNpmModule = rootProject.layout.projectDirectory.dir("generated-packages/${project.name}-js")
    val canonicalPath = pathToLocalNpmModule.asFile.canonicalPath
    implementation(npm("parsers-js", "file:$canonicalPath"))
  }
}
a

Artem Kobzar

11/24/2023, 5:50 PM
Copy code
// file: JsFaultReportLexer.kt
@file:JsModule("your-package/JsFaultReportLexer")

@JsName("default")
external class JsFaultReportLexer
But why don't you want to import them directly without the externals?
e

Edoardo Luppi

11/24/2023, 5:51 PM
But why don't you want to import them directly without the externals?
Mmm, what do you mean without externals?
a

Artem Kobzar

11/24/2023, 5:53 PM
You want to import a Kotlin declaration into another Kotlin module right?
Or
JsFaultReportLexer
is generated not from a kotlin source code?
e

Edoardo Luppi

11/24/2023, 5:53 PM
Oh no no. The files under
parsers-js
are generated by another tool (ANTLR). And unfortunately those use the ES6 module system, not CommonJS.
a

Artem Kobzar

11/24/2023, 5:55 PM
So yes, in this case, you could use the code I mentioned above.
gratitude thank you 1
e

Edoardo Luppi

11/24/2023, 5:57 PM
To have a clearer picture: I'm using ANTLR to write parsers. In a multiplatform context, ANTLR will output Java files for the JVM target, and JS files for the JS target. The JVM target is straightforward, the JS one is not so much. The JS environment where all of this will run is VS Code, which doesn't support yet loading ESM IIRC.
Tried the suggestion, and when running a dummy test case, I get
Copy code
Error [ERR_REQUIRE_ESM]: require() of ES Module C:\Users\edoardo.luppi\...\build\js\node_modules\parsers-js\JsFaultReportLexer.js from C:\Users\edoardo.luppi\...\build\js\packages\parsers-test\kotlin\parsers-test.js not supported.
Instead change the require of JsFaultReportLexer.js in C:\Users\edoardo.luppi\IdeaProjects\...\build\js\packages\parsers-test\kotlin\parsers-test.js to a dynamic import() which is available in all CommonJS modules.
    at C:\Users\edoardo.luppi\...\build\js\packages\parsers-test\kotlin\parsers-test.js:5:29
    at Object.<anonymous> (C:\Users\edoardo.luppi\...\build\js\packages\parsers-test\kotlin\parsers-test.js:18:2)
I might be better off transpiling those files to CommonJS with Webpack
a

Artem Kobzar

11/24/2023, 6:18 PM
Seems like the files use commonjs, instead of ES-modules
At least it uses
require
function inside
e

Edoardo Luppi

11/24/2023, 6:19 PM
Yup I can see the K/JS outputted test code uses
require
(I did call
useEsModules
btw)
a

Artem Kobzar

11/24/2023, 6:21 PM
Is it possible that you called something like
js("require(...)")
somewhere?
e

Edoardo Luppi

11/24/2023, 6:22 PM
I very doubt so, there is basically no code in
jsMain
or
jsTest
Snippet of the
parsers-test.js
file
Copy code
(function (root, factory) {
  if (typeof define === 'function' && define.amd)
    define(['exports', 'parsers-js/JsFaultReportLexer', './kotlin-kotlin-stdlib.js', './kotlin-kotlin-test-kotlin-test-js-ir.js'], factory);
  else if (typeof exports === 'object')
    factory(module.exports, require('parsers-js/JsFaultReportLexer'),
a

Artem Kobzar

11/24/2023, 6:45 PM
Where did you define
useEsModules
? Because seems like the file generated with UMD module system
All the generated files with ESM contain
.mjs
file extension
e

Edoardo Luppi

11/24/2023, 6:47 PM
Snippet of the plugin that does the configuration. See at the bottom
Copy code
project.kmpExtension.js(<http://KotlinJsCompilerType.IR|KotlinJsCompilerType.IR>) {
  moduleName = project.name

  nodejs {
    if (generatePackage) {
      // Generate the distribution packages under "/generated-packages".
      // This allows using the outputted packages as part of a workspace more easily
      distribution {
        val rootDir = project.rootProject.layout.projectDirectory
        val generatedPackagesDir = rootDir.dir("generated-packages/${project.name}")
        outputDirectory.set(generatedPackagesDir)
      }
    }

    testTask {
      useMocha {
        timeout = "0" // Override default 2s timeout
      }

      filter.isFailOnNoMatchingTests = true
    }
  }

  binaries.library()

  // Electron doesn't support ES modules yet. Switch to useEsModules() once it does.
  // See <https://github.com/electron/electron/pull/37535>
  if (jsNodeConfig.useEsModules.getOrElse(false)) {
    useEsModules()
  } else {
    useCommonJs()
  }

  ...
a

Artem Kobzar

11/24/2023, 6:48 PM
Could you try to add
useEsModules
inside the testTask, just to check if it works
e

Edoardo Luppi

11/24/2023, 6:48 PM
Sure
No difference + it messes up the source sets for some reason
Btw the prod compilation does indeed create a .mjs file. Seems like the issue is only for tests
a

Artem Kobzar

11/24/2023, 6:56 PM
Could you please create a ticket?
e

Edoardo Luppi

11/24/2023, 6:56 PM
Will do! Need to try to transpile those js files to CJS with Babel first
🙏 1
Thanks btw
Commented on https://youtrack.jetbrains.com/issue/KT-56818, which is similar. Let me know if I need a new issue tho.
9 Views