Hacker News new | ask | show | jobs
by jampekka 946 days ago
Makers of ESM were wilfully oblivious to sanity. CommonJS was/is one of the, if not the, best module and dependency systems out there.

https://gist.github.com/joepie91/bca2fda868c1e8b2c2caf76af7d...

4 comments

I find this mostly unconvincing (except for the part where all the tools messed this up and made the problem more difficult).

It's not clear to me how you get top-level await with CJS with significant drawbacks OR breaking changes that would just end up back up in ESM land.

The convoluted byzantine mess that is ES top level await is a self-inflicted problem stemming from the ESM bizarre pipe dream that static import won't be a blocking operation for code that depends on the imported code.

With blocking require there's of course no problem with top level await. And having a thenable import doesn't even need any language level support.

Yeah, you can parallelize static imports. But you can just as well doing this by having a semantically blocking require that is free to of course do whatever it wants as long as the execution is sequential in effect.

If there's some dynamic trickery the implementation can't figure out, just revert to blocking and nag in the console.

Being imperative instead of declarative, it hinders a host of key optimizations like parallel loading and dead code elimination
This is discussed in the link I posted. Bundlers manage to statically analyze CommonJS just fine. With require("stringliteral") and exports.thing = thing, that cover 99.9% or so usage, this is just as easy to statically analyze as ESM.

Saying that you need declarative for static analysis is like saying tail call optimization is impossible.

And if a stricter module system would be still required, it could have been quite easy to make compatible with, well, require.

Require is fine but I totally hate the CJS exports. Feels like writing '90s era Perl code. On the other hand ESM dynamic imports like

  import('this').then(r => that()) 
rather suck.
The post dismisses native browser support (a huge benefit of ES modules) as "an utterly useless feature" that's "unavoidably slow" because of the additional roundtrips needed as the dependency tree is traversed.

But this problem has been solved by modulepreload[0], also natively supported in the browser, which lets you specify the modules upfront in the HTML, avoiding the need for additional roundtrips. Tooling could help with generating the list of preloads but is not necessary.

[0] https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes...

Tooling could also analyze the dependencies and create appropriate bundles of the code to avoid roundtrips. Actually such tooling has existed for over a decade and are used by practically all but simplest Javascript applications.

I'm all for development without a compilation step. I've written hacks to resolve node modules and transpile in browser to avoid compilation. Implementing prefetching even with this hack would be less hacky than going full circle and injecting HTML tags to do the prefetching.

ESM is like XML: The problem it solves is not hard, and it doesn't solve the problem well. Actually ESM it isn't even close to solving the problem after trying for almost 15 years.

As you say, tooling can solve the problem. But with ES modules and modulepreload, we can now (contrary to what the post argues) also solve it without the tooling, which is an improvement.

Yes, all but the simplest applications currently use the tooling-based solutions, because there was no other way. But now that we have an alternative solution, perhaps all but the most complex applications will manage just fine without tooling, using just the built-in module support.

So without tooling you need to specify the preload tags by hand. And of course import the modules in the JS.

A simple sync and/or async function/statement would had solved the problem of having modules in the browser. E.g. RequireJS solved this in 2010 or so. There were straightforward proposals for native modules even before this.

> So without tooling you need to specify the preload tags by hand. And of course import the modules in the JS.

Yes. This is exactly what I want. It's helpful for simple apps, and without all the toolchain complexity more apps can be simple.

ES modules solve the problems I care about:

1) Eliminating the extra roundtrips.

2) Nice and simple syntax.

3) Tooling not required.

RequireJS solved 1 and 3 over a decade ago and better than ESM.

2) ESM syntax is objectively more complicated than CJS while managing to be less expressive. It's one of the most complicated module syntaxes out there. Subjectively it's not nice at all, much due to the objective difference.

ESM is a defined default you find back in ECMA specifications. That is why everybody should migratie. Node.je is also moving ESM to the default. If some systems dont do it, dont use it anymore. That it went i dont use jest anymore, and Node.js has also now a good test runner. (Useful for packages and backend systems, i think for frontend systems with eg React there are better test suits to help with special frontend stuff like the DOM)