Hacker News new | ask | show | jobs
by spankalee 1687 days ago
This post is terrible, actually.

CommonJS was never going to be natively supported in browsers. The synchronous require semantics are simply incompatible with loading over a network, and the Node team should have known this and apparently (according to members of TC39 at the time) were told their design would not be compatible with future a JS module standard.

So the primary thing that JS modules fix is native support, and for that you need either dedicated syntax or an AMD-style dependencies / module body separation. AMD is far too loose (you could run code outside the module body), so dedicated syntax it is.

Everything else flows from there. I really hate how people blame the standards instead of the root cause which is Node not having taken the browser's requirements into consideration. Culturally, I think that's mostly fixed now, but it was a big problem early on in Node's evolution.

3 comments

Yes but who cares of native support in the browser? I mean, most JS stuff nowadays is transpiled, written in TypeScript, or if written in plain JS still transpiled anyway to support older browsers, and bundled in a single optimized file.

Loading all the dependencies over the network to me is just inefficient, you will have hundreds of requests instead of a single one, you will load the full source not a minified and optimized one, I just don't see the point.

Native support matters so that we're not eternally required to use tools for even the simplest of cases. Being able to write two files with one importing the other with no npm or bundler in sight should absolutely be a feature of the native platform.

And yes, in production you probably will want to bundle, but you probably also want to minify. Does that imply that we should require a minifier to even run any code at all, even in dev? No, of course not.

By adding a standard and native support we allow for sites that work without bundling and bundling that can adhere to the standard and not have to even be configured because the input is standard and the output must preserve those standard semantics. That gives tool independence and simplifies usage of the toolchains, and that's a great goal to shoot for.

If the project is not so complex, you don't need modules at all. You can just do like we did in the old days (and I still indeed do for simple projects like mostly static sites) and load your JS with `<script>` tags. You can have multiple files, of course everything must be in the global scope to use that but still you can do that.
Even simple single-file use cases may benefit from ESM support for top-level await. I thought I had heard of a proposal for supporting top-level await in script mode, but I can’t find it and it probably wouldn’t be feasible anyway because it implies the whole script, which would otherwise be blocking, is async.

That said, nothing is preventing anyone from using type="module" (or .mjs) for uncompiled code. In fact I’m doing this on a project specifically to bootstrap on-the-fly ESBuild TypeScript compilation.

But it doesn't matter for Node, since async ESM is a superset of sync CommonJS. Simply putting await before every ESM import gives the Node semantics.

So, isn't adding ESM to Node is strictly a new feature? What am I missing?

That’s a nice move you do here. Node claimed guilty for implementing the least sucking module system at the time, then growing its NPM size to the skies, and now when there is a ready-to-use module for every task out there, let’s raise a finger and claim they were doing it wrong. Uh oh.

Web standards are goddamn late for 20 (twenty) years and it’s not their moral right to decide what should be broken or deprecated, sitting on the top of the mountain of working code which a workhorse named “node” produced in less than a decade.

So what would you have had TC39 do, just never standardize a module system because Node made one first that was incompatible with the web?