jsinterop-generator icon indicating copy to clipboard operation
jsinterop-generator copied to clipboard

Operating on non-externs?

Open niloc132 opened this issue 4 years ago • 5 comments

I'm experimenting with taking Closure-annotated JS (both hand-written and generated from TypeScript via tsickle). I was surprised to discover that when the generator runs, it only emits jsinterop types that represent the provided externs - "actual" JS gets no java generated for it.

As an example, take a j2cl-based project with closure-compatible JS libraries that need to be used/consumed from Java. Feeding non-@extern js results in no output (and a chmod error in format_jarsrc), probably because jsinterop.generator.closure.ClosureJsInteropGenerator#generateJarFile removes non-extern files - removing that check results in the tool trying to also generate java for dependencies (like elemental2-core+promise, which effectively every library must depend on).

--

Going a bit further: It appears from poking around in the repo that I might just be thinking about this wrong or using the tool wrong, and that this isn't a real limitation. For example, DependencyFileWriter specifically calls out TypeScript rather than closure externs?

This file makes the link between typescript type and java type and is used when a generated library is used as dependency of another generated library.

Are typescript .d.ts files intended to be supported, specifically those generated via tsickle? The sample I tried nearly works, at least with this one addition to jsinterop.generator.closure.ClosureJsInteropGenerator#createCompilerOptions:

options.setLanguageIn(ECMASCRIPT6_TYPED);

but even then the library I'm working with hits errors, whether operating tsc-generated .d.ts files, or ones emitted by tsickle (and for the record, I've been unable to get tsickle yet to emit actual externs, it only wants to provide either .mjs files with an empty externs.js, or .js+.d.ts. Perhaps it works better if you avoid bazel, but my goal was to stay within the ecosystem).

--

Are these use cases intended to be supported, or am I just doing it wrong?

niloc132 avatar May 21 '20 20:05 niloc132

Whoops - I misread the isExtern() filter, the code reads as the opposite of what I understood it (though the effect is the same), because that isExtern check is unrelated to closure's notion of externs. Instead, this is sort of "is this type a first-party type, given to the generator as an extern, and not from a dependency mapping file".

I can clearly see that when you give the generator non-externs, it parses them, but the point of the bug still stands: java files don't seem to be emitted for them, so writing java code against those types doesn't make sense (short of my current approach: run tsc/tsickle to turn .ts into .d.ts+.js, run tsickle again to turn .d.ts to externs, manually edit the confusing bits, and then run jsinterop-generator).

niloc132 avatar May 22 '20 18:05 niloc132

Yes, isExtern method there should be renamed, it is not really related Closure extern.

Starting from Closure then using tsickle back and forth to generate js externs to generate Java wrapper will not work. Because your Java code will try to treat Closure code as externs files and J2CL will not generate proper goog.require for them.

Going to your original question and follow up questions: JsInteropGenerator was updated at one point to experiment with Closure libraries (i.e. non-externs) however we never completed that work. We also had a version of generator that directly worked on d.ts files but we couldn't keep it up to date and it is never open sourced.

gkdn avatar Jun 08 '20 22:06 gkdn

Okay - disregarding typescript, I do have npm+node+bazel wiring that seems to work to take .ts code, generate .d.ts files from them, use tsickle to emit externs (and never feed them to closure), and use jsinterop-generator on those externs to emit generated @JsType(isNative=true) java, so that gwt2/j2cl projects can interact with the original .ts/.js. Some manual changes are required after externs are emitted, mostly related to #45.

Because your Java code will try to treat Closure code as externs files and J2CL will not generate proper goog.require for them.

In GWT2, this shouldn't matter, correct? This mostly applies to j2cl wanting to see a proper namespace instead of a deeply nested name? And to reiterate, since I'm not feeding the .ts to closure (long story, but the short version is that only some of the .d.ts files are generated from .ts, some are handmade, distributed via npm, and describe existing .js files which aren't closure compatible), so telling closure that they are externs is unfortunately accurate.

niloc132 avatar Jun 08 '20 23:06 niloc132

Yes, it never matters for GWT2 because for GWT2 all javascript is external.

For J2CL, some JavaScript is external some is not. External is code is that that is not fed into Closure compiler and they require externs. Such code is assumed to be global namespaces and not obfuscated; so no goog.require is needed.

In short, if you don't supply your npm+node+bazel stuff to closure, use externs + jsinteropgenerator to access them from J2CL it should work.

(But if your npm+node+bazel stuff is behind the scenes using closure or other obfuscation mechanism, then you will be potentially creating new set problems (e.g. name collisions)).

gkdn avatar Jun 09 '20 02:06 gkdn

Got it, thank you.

Are there any avenues by which I could help contribute to this effort, either the work that was already started, or a new approach based on the work that was previously attempted? While the specific use case I described appears to work for my specific needs, there are a lot of people interested in finding a way to consume other TS libraries into GWT2 or J2CL.

niloc132 avatar Jun 09 '20 02:06 niloc132