Live data from Hacker News

ES modules are terrible

gist.github.com

111–120 of 175 posts

Re: ES modules are terrible

#111
There is a valid discussion to be had about whether the Node.js ecosystem disruption of moving from CJS to ESM is worth the benefits, but the assertion that it's technically worse isn't accurate. A few things ESM does better in Node.js:

1. Asynchronous dynamic import() vs. blocking require(): allows the program to continue while a module is being dynamically loaded.

2. Circular dependencies: ESM correctly resolves most of them, while CJS does not. [example below] I believe this is possible because ESM top-level imports and exports are resolved before JS execution begins, while require() is resolved when called (while JS is already executing.)

3. Reserved keywords `import` and `export` vs. ordinary identifiers require, exports and module: Allows tooling to be simpler and not have to analyze variable scope and shadowing to identify dependencies.

I haven't really encountered #3, but I can say I've benefited from #1 and #2 in real-world Node.js projects using ESM.

----

Circular dependencies example:

   // a.js
   const b = require('./b.js');
   module.exports = () => b();

   // b.js
   const a = require('./a.js');
   module.exports = () => console.log('Works!');
   a();
Running this with "node b.js" gives "TypeError: b is not a function" inside a.js, while the equivalent ESM code correctly prints 'Works!'. To solve this in CJS, we have to always use "named exports" (exports.a = ... rather than module.exports = ...) and avoid destructuring in the top-level require (i.e. always do const a = require(...) and call it as a.a() elsewhere)

Re: ES modules are terrible

#112
post #51

Earlier quoted context omitted.

Bundling will not go away as it solves a different problem of how to best distribute the app to final users. When authoring code, you want to have many small files so you can keep related logic blocks isolated from the rest. When distributing the code, you want to ship a few larger files to reduce network overheads. Any non-trivial frontend app will call code from 100+ files, and your browser is tuned to request thes…

Worse, even if they were all fetched in parallel, you would still see terrible loading times simply because a dependency graph can only be traversed depth-wise serially. Doesn't matter what network protocol you use or how parallel it is.

This is not true at all. For every module you can parse out and load the module's imports in parallel. As you traverse the graph the known and loadable module frontier can grow much wider.

The only way it would be serial is if every module only imported one other module.

Re: ES modules are terrible

#113
post #81

Earlier quoted context omitted.

tell me that doesn't improve load times It will negatively impact load times. Either you only bundle what is needed on the current page. Then the next page will load slower because it needs to bundle all those modules again as it uses a slightly different set of modules. Or you bundle everything used on any page your users might go to during their session. This will give you a giant blob that has to be loaded upfront…

That's true if you create your own bundle yourself. Famous frameworks like Next.js or Nuxt make much smarter bundles, with common dependencies grouped together, and bundling the rest by page/view, then loading each bundle when needed.

It’s still a tradeoff, though. Let’s say a website has three pages: /a, /b and /c. Two of those pages, /a and /b, each use the module `foo`. Where should `foo` get bundled? If you put it in the “common” bundle, it’ll get served to /c even though it’s not needed. If you put it in both of the bundles for /a and /b, the client will download it twice.

Re: ES modules are terrible

#114
post #103
post #92

Earlier quoted context omitted.

I believe sharing cached code between domains has been almost entirely eliminated by browsers now, because it turned out to be a huge privacy leak: a malicious domain could attempt to load code that was used by another domain, time how long it took to load and use that to determine if the user had visited that other site. Browsers fixed this by making the browser cache no longer shared between domains.

Hm, I wonder if this could be circumvented by doing timing attacks against the CDN cache? That's still shared between domains...

It is not:

https://developers.google.com/web/updates/2020/10/http-cache...

https://developer.mozilla.org/en-US/docs/Web/Privacy/State_P...

Re: ES modules are terrible

#115
post #57

Not sure it the author tried a new build tool like vite, esbuild and so on. Working on large projects and having everything first loaded and then you can load it in the browser is a waste of time that every web developer has every day. Some real world times FOR DEVELOPMENT: Storybook first load: 90 sec, Storybook after first load changes: 3 sec, Vue App first load: 63 sec, Vue app change after that: 5 sec, Vue App wi…

It’s usually senior devs who don’t want to learn new things just because these are new. And aren’t afraid to use 3 years old packages (how dare they!). Imagine having a subroutine which is “done” doesn’t get updates for years, laughable! Who makes it “done” when you can fuck it up at the start and fix a little every day, filling that activity grid with green dots.

All of this js-related stuff is just a fast fashion, the bad part is web developers are locked into it with no chance to relax and to just create their boring services and apps.

Re: ES modules are terrible

#116
post #14

> And then people go "well you can statically analyze it better!", apparently not realizing that ESM doesn't actually change any of the JS semantics other than the import/export syntax, and that the import/export statements are equally analyzable as top-level require/module.exports. ... "But in CommonJS you can use those elsewhere too, and that breaks static analyzers!", I hear you say. Well, yes, absolutely. But tha…

This isn’t a real concern, and yes I’ve written a static analyzer for requires (as have many build tools). The fact of the matter is no one is trying to trick the analyzer by passing variables to require, or even more mischievously trying to rename require or something (there aren’t a lot of “(a => a)(require)(path + “/x.js”)” out there). In practice, it is used like a static feature, and when it isn’t, it’s for a go…

> to accommodate a set of restrictions designed with the browser in mind

That's the whole point, and a very good thing.

Re: ES modules are terrible

#117
Here is why ESM is better for static analysis than CJS:

    module.exports = {
        get foo() {
            const otherModule = require('equally-dynamic-cjs')
            if (otherModule.enabled) {
                return any.dynamic.thing.at.all
            }
        },
        get bar() {
            this.quux = 'welp new export!'
            return 666
        },
        now: 'you see it',
    }

    setTimeout(() => {
        console.log(`now you don’t!`)
        delete module.exports.now
    }, Math.random() * 10000)

    if (Date.now() % 2 === 0) {
        module.exports = something.else.entirely
    }
You can, of course, achieve this sort of dynamism with default exports. But default exports are only as tree-shakeable as CJS. Named exports are fully static and cannot be added or removed at runtime.

Edit: typed on my phone, apologies for any typos or formatting mistakes.

Re: ES modules are terrible

#118

> for some completely unclear reason, ESM proponents decided to remove that property. There's just no way anymore to directly combine an import statement with some other JS syntax This is one of those 'worse is better' things in language design, I believe. It guarantees simplicity, traded off against extra verbosity. In fact, when it comes to the common and probably most valuable case of reading and understanding cod…

> This is one of those 'worse is better' things in language design, I believe. It guarantees simplicity, traded off against extra verbosity.

And with top-level await the restriction goes away (albeit the ESM equivalent is still a bit more verbose).

    (await import('anything'))(...yup)

Re: ES modules are terrible

#119
post #13

Problem is bothed node.js implementation that leaves most of existing applications without migration path. Even today it is not possible to create full ESM application front or backend. It is worse than python 2 to 3.

Node ESM support has gotten a lot better through versions 12-17. The biggest problems for workflows that currently work “well” for CJS are:

1. --experimental-loader is more complex and less stable than --require. But it’s also a lot more robust.

2. There’s no equivalent to the require cache, which makes mocking and long running processes like watch mode challenging. This is partly a benefit, as it discourages cache busting patterns like those used in eg Jest which create awful memory leaks.

Re: ES modules are terrible

#120

The reasoning presented is only valid if you are stuck holding a bunch of dependencies making use of old conventions. At that moment the complaints about the module approach become a very real concern. That said the problem isn’t modules are all. It’s reliance on a forest of legacy nonsense. If you need a million NPM modules to write 9 lines of left pad these concerns are extremely important. If, on the other hand, y…

And yet, the reality IS that 90% of the web is using legacy stuff - heck, even something like 50% of the web still has jQuery on it. (haven't checked the figure in a while, but I guess it is still close to that figure). I think the true anger is that something so essential and basic to JS development has this giant breaking change if you want to switch over to ESM - there's no reverse compatibility or fallback - it j…

The solution is some soul searching. Do you really need Babel and Webpack to build a web app? The answer is of course an astounding YES! Most developers cannot add text to a page without JSX, which therefore means React and everything it requires.

So when you dig even deeper this is really a people and training problem.

Post reply on HN