nim javascript backend generates a single .js file:

https://nim-lang.org/docs/backends.html#nimcache-naming-logic-nimcache-and-the-javascript-target

There are no other temporary files generated, the output is always a single self contained .js file.

could it use the new javascript ES6 modules (say as an option)? It's supported on all modern browsers

https://www.contentful.com/blog/2017/04/04/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling/

or is outputting to webassembly making all this obsolete?

2018-04-15 01:15:12
Sure it could use ES6 modules but that would be quite some work to implement and what would be the gain? 2018-04-15 06:00:03

I wondered how difficult it would be to create a JS backend that converts the Nim AST to the ESTree spec AST and then use the resulting ESTree spec AST to generate the Javascript with a tool like ESCode Gen.

The benefit is generate very clean JS code in any desired target version. It would also mean access to other JS module tooling. The downside is its probably a lot slower than the Nim JS backend.

2018-04-17 00:05:52
@honhon you could already run the existing JS through a prettifier, no? I think the results would be the same. 2018-04-17 10:18:25

Sure it could use ES6 modules but that would be quite some work to implement and what would be the gain?

For a completely nim-based app, not a lot. If you're incorporating the result into a larger site or trying to do something like code splitting I expect the js-based tools will work better with the output split into modules but I wouldn't be confident advocating for it without testing stuff out. It's something I've thought about exploring but there's much lower hanging fruit in terms of js output.

you could already run the existing JS through a prettifier, no? I think the results would be the same.

The output of the js backend isn't particularly elegant and I think it'd need a cleanup pass before it'd look good after a prettifier. I hacked up the jsgen part of the compiler last month to output closure compiler compatible code. As long as I -d:release to get rid of the frame tracking cruft and run the code through closure advanced mode I can get reasonably good code out of the js backend.

The major remaining piece that's holding back the output js from being what I would consider to be good is nimCopy. The need to reflect on types causes jsgen to emit the full set of types. Using the Karax todoapp as an example, after passing through closure advanced ~60% of the app is this type information. I estimate the output would be ~16k instead of the current 96k and the 5.6k gzip gets Karax into the competitive range with js micro frameworks. Further, nimCopy is polymorphic and mutually recursive and I'm pretty sure that's preventing JIT inlining. I think the compiler has enough type information to emit multiple monomorphic implementations which should eliminate the need to have the type information in the output for some fraction of the nimCopy uses and I think a lot would get inlined and eliminated by the closure compiler's optimizations.

I got tripped up on working on this because nimCopy is used to implement a number of behaviors beyond just deep copying (something with return values, etc) and I don't have a firm enough grasp on nim semantics to know if I'm breaking stuff or not. I got distracted by another project and never came back around to working on Nim's js output. I think there's likely a real need for the full polymorphic mutually recursive version but I have a hard time telling.

The lesser remaining piece is some way to set all strings in a module to be parsed as cstrings instead of nim strings. I get why the decision was made to have nim strings as the default but there's a varying (depending on app size) amount of activity at startup that's simply converting string literals from JS into nim strings only to convert them back to js strings for use/display. The converting back and forth is inefficient but I really don't want the activity at startup because it delays time to interactive.

The compiler has a nice tracking system to convert cstrings into nim strings for things that need nim strings so everything works fine if you remember to declare everything to be a cstring but it requires vigilance on the part of the developer. I think a large fraction of nim client apps wouldn't need nim strings at all.

With those out of the way, I think the biggest gains would be adjusting Karax. I started hacking on the js backend because I think Nim has enough type information and compile time programming that I could separate out the non-changing nodes from the changing nodes, compile the static parts to html templates, and get pretty significant wins in pretty much every metric but I got sidetracked by the js output.

For compiler tweaks beyond the runtime ones I outlined, the top of my list would be source map support, which would allow the elimination of the frame tracking stuff. I'd want to double-check the other runtime functions to make sure they're efficient. There's also a mismatch in js language support. The example that comes to mind is the code emitter outputs a set of typed array polyfills but doing async stuff emits async/await and nothing supports that without also supporting typed arrays. I noted a few other things like this but the issue is more of a social issue than a technical one.

Well, that went on longer than I anticipated when I started. I wanted to have more to show and be able to make a firmer commitment to fixing things before complaining but the post is written so I might as well complain now.

2018-04-17 21:40:02
Well its not code formatting. The output would be more idiomatic javascript, the ability to target different versions of javascript, less verbosity, and reuse of other tooling. 2018-04-18 12:54:31