NPM and NodeJS should do more to make ES Modules easy to use (2024)

7 points by alurm


paulirish

Not necessarily relevant, but Boris is the lead of Claude Code. Seems like he wrote this as it was first taking shape.

alper

I'm not deep enough into the problem but that they've let this fester for so long with so little progress on the topic is really disappointing.

epidemian

Yep, the current NPM ecosystem seems to be kind of a mess in this regard. I've recently tried switching our TypeScript front-end project at $WORK to "type":"module", to avoid having some config files needing an .mjs extension instead of .js, and failed spectacularly at that seemingly simple task.

The main problem was something so byzantine that i find it difficult to explain: NPM dependencias that use ESM syntax on their source code, but actually published CJS module syntax to NPM (they did a Babel/Webpack conversion when publishing), AND also have a default export that is not an object, but something else, like a function for example. Turns out we had a couple of dependencies that met this criteria. What happened then was, when importing these external dependencies on a ESM module (TS files on our type:module project), something like import setupLogRocket from 'logrocket-react', TypeScript/esbuild converted the accesses to that setupLogRocket function into accessing the .default attribute of what the module.exports transpiled dependency exported, and thus caused a runtime error by trying to call an undefined thing.

Note that TS typechecking didn't complain; according to TS and the dependency's typing declarations, that import is fine. It was just an unfortunate combination of how nodejs + TypeScript + esbuild work, and the way these dependencies are declared/transpiled.

Phew, that was a mouthful to write. Suffice to say, i abandoned the idea of "migrating" the project to type:module. I'll live with the few .mjs files for now. And if i ever need to come back to trying this out in the future, i'll probably end up vendoring the code of any dependency that doesn't work (all the offenders seem to be rather small libs), so that we can control how its source code is interpreted by these tools.

Buy yeah, this sort of stuff is definitely way more complicated than it should. And there doesn't seem to be a strong consensus on what the correct path forwards is :/

conartist6

Note that the really big problem, not being able to access ESM modules from CJS modules, has seen progress since this was written.

hongminhee

This is exactly why I use Deno and JSR. Of course, they have their own set of problems, too.

Johz

Interestingly, I believe NodeJS did none of the suggestions given in this article, and instead fixed the problem another way: require(ESM) (that is, importing an ESM file from a CommonJS file) is now supported in most cases*, which means people can upgrade as and when they need to, without worrying about transient dependencies still being CJS-only.

* The only case that isn't supported is if an ESM file uses top-level await anywhere in the call stack — but this is rarely seen and highly discouraged in modules anyway.