Hi. I have an issue with using kotlin collections ...
# javascript
e
Hi. I have an issue with using kotlin collections in typescript. When I generate esModules and typescript declarations, a custom js module is created. It includes • the declarations for mine and 3'rd party classes, • the main mjs file with implementation of my classes, • modules for the libraries that are being imported in the main mjs. The issue is that I can't access KtMutableSet and other kotlin collections from this module. The types are being imported into the main module file, but they are never exposed further. Also, there is no separate module for kotlin-stdlib. What am I expected to do? Should I create a script that will make a module out of the kotlin-stdlib? Here is a small project that represents the issue: https://gitlab.com/yevheniif/kt-collection-test
a
Hi @Eugene Fedorenko Yep, that's true, we consider this issue. https://youtrack.jetbrains.com/issue/KT-65695 As a work-around, for right now, you can import the
KtMutableSet
from the
kotlin-stdlib.mjs
e
Well, that's sad. But thanks for sharing
I've added a comment to the issue describing my case. It really brakes the collection types interoperability feature when using es modules. It would be great if the issue will gain some priority
e
@Artem Kobzar trying out the reproducer that was posted on the issue I've also noticed that ESM mode does not play nice with
whole-program
granularity, which could have been a workaround. There is no output from compilation both in 2.1.21 and 2.2.0-RC.
a
Oh, wow. It definitely sounds like a bug. Could you please share this reproducer in the ticket?
e
AHHHH wait, nope, my fault. What happened is the original project was using
kotlin("js")
plugin, and I moved it to
kotlin("multiplatform")
, but forgot to adjust the sources from
src/
to
jsMain/kotlin/
@Eugene Fedorenko try adding
Copy code
kotlin.js.ir.output.granularity = whole-program
to your
gradle.properties
file as a workaround. The
Kt*
objects should be exported at that point.
e
Yes, it is a workaround that works, but it substantially increases the size of the js script a browser need to fetch. We can't take this throwback in our case
a
I believe that the "right" way to fix it from our side (and it's what we're working on) is to generate d.ts files per module (per file for the per-file granularity). Unfortunately, the re-export in ES modules (in per-module granularity) can't play well because of the name clashes.
thank you color 1
👍 1
e
but it substantially increases the size of the js script a browser need to fetch
As far as I understand, the browser would end up fetching all the modules anyway, even in a per-module scenario, as you're not using dynamic imports.
o
As far as I understand, the browser would end up fetching all the modules anyway, even in a per-module scenario, as you're not using dynamic imports.
The workaround with
kotlin.js.ir.output.granularity = whole-program
should work for us, but we would like to use
per-module
because it seems more flexible in terms of code-splitting. Currently, we bundle our code and kotlin libraries to the separate chunks assuming that kotlin libraries change not frequently(only when version is updated or DCE changes the output).
e
I'm wondering if you've considered the compiler performs variable name mangling, and also I'm not sure if the generation of additional variables uses stable names, or if they change from run to run. This is a topic I'm not knowledgeable enough for an answer, as I'm deploying to Node.js and normally don't care about it.
o
We do not have the priority to optimize caching and check it if works properly, but I would like to have more flexibility with
per-module
approach. I've thought that DCE and mangling should produce the same result for the same input.
the generation of additional variables uses stable names
In case of vendor code, this happens when DCE changes the output, doesn't it? Or do you mean that additional variable in non-vendor code may change the result of mangling in vendor code? We did not check this case as well, but this is a good point.
e
I meant to say that something like this
Copy code
public fun <T, R> array(values: Array<T>, transform: (T) -> R): Array<R> {
  val array = Array<Any?>(values.size) {
    transform(values[it])
  }
  return array.unsafeCast<Array<R>>()
}
compiles to
Copy code
function array(values, transform) {
  var tmp = 0;
  var tmp_0 = values.length;
  // Inline function 'kotlin.arrayOfNulls' call
  var tmp_1 = Array(tmp_0);
  while (tmp < tmp_0) {
    var tmp_2 = tmp;
    tmp_1[tmp_2] = transform(values[tmp_2]);
    tmp = tmp + 1 | 0;
  }
  var array = tmp_1;
  // Inline function 'kotlin.js.unsafeCast' call
  // Inline function 'kotlin.js.asDynamic' call
  return array;
}
And I'm not sure if generated intermediate variables like the
tmp*
ones use stable names or if the compiler generates different names from time to time. Or if non
JsExport
-ed exported declarations are stable or not:
Copy code
var VOID = kotlin_kotlin.$_$.b;
var captureStack = kotlin_kotlin.$_$.q1;
var initMetadataForClass = kotlin_kotlin.$_$.b2;
var get_indices = kotlin_kotlin.$_$.d1;
var IllegalArgumentException = kotlin_kotlin.$_$.l3;
var toString = kotlin_kotlin.$_$.m2;
var Unit_instance = kotlin_kotlin.$_$.j;
var get_lastIndex = kotlin_kotlin.$_$.f1;
var ArrayList = kotlin_kotlin.$_$.k;
Like those
k
,
f1
, etc. Although that's CJS, but I guess the same reasoning applies to ESM.
o
Mangling/minification for JS should be stable for the same input, otherwise it breaks browser caching. I found this issue describing the bug with stable minification of function names. Though, we have not checked yet if it works in our case due to the lack of priority for it for now.
e
Yup that was it. I know you want to optimize loading using code splitting now, but the point is I'm not entirely sure you're gonna be able to do that. I would honestly prioritize DX and go with
whole-program
now, while the K/JS team attempts to fix the underlying issue. If six months from now it's not fixed yet, then I'd begin experimenting with
per-module
and caching effectiveness.