Another Question ... Can we not export enums? The ...
# javascript
b
Another Question ... Can we not export enums? The docs for
@JsExport
indicate that we cannot:
Copy code
@ExperimentalJsExport
@Target(allowedTargets = [AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.FILE])
expect annotation class JsExport(source)
Exports top-level declaration on JS platform.

Compiled module exposes declarations that are marked with this annotation without name mangling.

This annotation can be applied to either files or top-level declarations.

It is currently prohibited to export the following kinds of declarations:

expect declarations
inline functions with reified type parameters
suspend functions
secondary constructors without @JsName
extension properties
enum classes
annotation classes
When I add
@JsExport
to a basic enum
enum class Num { ONE, TWO, THREE }
... it compiles OK, but when I try to use it in javascript (typesript) code, I get a runtime error
Cannot read properties of undefined
when I import and use it, like
const one = Num.ONE;
e
@JsExport
KDoc is outdated, and K/JS docs too (tho, to a lesser extent, and it will be fixed). https://github.com/JetBrains/kotlin-web-site/pull/4831
I get a runtime error
Could you create a minimal example? It should work.
b
Let me see what I can do ...
e
This works under 2.1.21.
b
So, this configuration works fine:
Copy code
js {
    browser {}
    binaries.library()
    generateTypeScriptDefinitions()
}
But using es2015 modules does not ...
Copy code
js {
    browser {}
    
    // enums stop working with es2015 modules
    useEsModules()

    binaries.library()
    generateTypeScriptDefinitions()
}
e
How are you importing the declaration from the TS code?
b
with the first configuration, I would do something like:
Copy code
import * as kmp from '@my-org/shared';

// And then
const myClass = new kmp.org.my.shared.MyClass(kmp.org.my.shared.Num.ONE);
with the second configuration it looks like:
Copy code
import { MyClass, Num } from '@my-org/shared';

// And then:
const myClass = new MyClass(Num.ONE);
e
Could you open up the Kotlin module's outputted JS file under
build/dist/js/productionLibrary
, scroll all the way down, and paste here the
export
section?
b
Sure, for the configuration with es2015 modules?
e
Yup!
b
ok, one sec
Copy code
export {
  MyClass as MyClass,
  Num as Num,
};
e
So the
Num
instance exists and is indeed exported. Are you sure the error refers to
Num.ONE
?
b
And just above that, I have:
Copy code
Num.values = values;
Num.valueOf = valueOf;
defineProp(Num, 'ONE', Num_ONE_getInstance);
defineProp(Num, 'TWO', Num_TWO_getInstance);
defineProp(Num, 'THREE', Num_THREE_getInstance);
yeah, I can do:
Copy code
const one = Num.ONE;
And it'll give me
Cannot read properties of undefined (reading 'ONE')
on that line. and if I change it to:
Copy code
const one = Num.valueOf("ONE");
it'll give me:
Cannot read properties of undefined (reading 'valueOf')
e
How's the final JS bundle that you get from compiling the TS project?
It looks like there is something wrong with the TS project setup.
b
hmm well I am using Angular.
😄
it's their basic "hello world" app.
e
How are you linking the outputted Kotlin JS distribution to the Angular project?
b
I'm installing it as an npm package ...
Copy code
$ npm install file:/path/to/my/project/shared/build/dist/js/productionLibrary
(for now this is all running on my local machine)
e
That's a strange situation. I mean, I would expect all of this to work. Angular creates a bundle by inlining the content of the Kotlin library, so I would really check if the outputted bundle contains your
Num
declaration.
b
Yeah, I'll check on that. I agree it seems fine. All of the generated js code looks fine. I'm really not sure why it doesn't work when I enable es2015 modules.
e
I don't know either. Let me know what you find in the bundle, there might be guesses there.
b
ooh ... progress
need to do a little more testing, but this may have been an issue with angular/typescript caching.
e
I feel like the real problem is npm with the
file
protocol. I always avoid it as it causes stale files to be used.
b
could be. At the moment, I'm just setting up a basic Proof of Concept. I have a multiplatform library with business logic that we've been using for or ios and android apps. And now I'm experimenting with compiling it for JS as well and using it in our (angular) web app.
It does seem to be working now. Now I'm on to a new error where it can't see
KtList
... Probably another caching issue, because that was working before.
e
Nope, that's https://youtrack.jetbrains.com/issue/KT-65695 most likely. @Artem Kobzar seems like it is becoming very common to hit this issue now.
b
interesting. oh .. I bet it was not working before. I'm just seeing it now because at runtime it's finding my enum now and getting past that line, and then failing on the line thats trying to use KtList.
@Edoardo Luppi so is using the
whole-program
granularity still the workaround? I see on that ticket that you mentioned that
per-file
is not yet stabilized (as of 9 months ago).
e
As far as I understand, yes, that's the only workaround at the moment.
OR. You build using
useCommonJs
, or the default UMD.
b
right
whole-program
does seem to work. Regarding your comment:
There is no real difference between using
whole-program
and
per-module
when consuming it.
The difference might be in incremental compilation (where
per-module
might be faster) and output size (where
whole-program
might perform better DCE).
If I understand that correctly, there's no real "cost" to using
whole-program
from the client (web app) side. It's really just during development on the kotlin side, it could slow down build times a bit. Is that right?
e
That's correct. Angular will always bundle and get rid of what's unnecessary anyway.
b
👍 Well I'm back on track now. Thanks for all your help!
If anyone else happens to come across this thread and has the same issue with Angular and
file:
based npm dependencies ... The solution for me was do delete the angular cache in my web project, and then re-install the npm package:
Copy code
$ rm -rf .angular/cache
$ rm -rf nodde_modules/@my-org
$ npm install