Besides, it's already going through one transpilation step to go from TS to ESM, so adding a second one for CJS really isn't that much hassle.
I think if node.js had made require() work with ESM, I could probably drop CJS. But since that's probably never going to happen, I'm just going to continue shipping both versions for old projects and not worry about it.
Nobody is arguing for that. Once you ship ESM, you can continue shipping ESM.
In Node 22 you can even require() ES modules (with an experimental flag, at the moment)
> Nobody is arguing for that. Once you ship ESM, you can continue shipping ESM.
I'm not sure I follow you there. I did continue shipping ESM.
> In Node 22 you can even require() ES modules (with an experimental flag, at the moment)
Oh, I didn't know about that, cool! Once it becomes un-flagged I might consider dropping CJS.
Why is making downstream have to switch to `await import()` that big of a deal?
You can use async/await in CJS just fine. Sure, sometimes you may need to resort to some ugly async IIFE wrappers because CJS doesn't support top-level await like ESM does, but is that really such a big deal?
Sure, it's a breaking change, but that's what semver major bumps are for.
I just think that if projects want to stay in CJS they should learn how to use async/await. I clearly don't understand why CJS libraries feel a need synchronous require() for everything. (Though to be fair, I've also never intentionally written anything directly in CJS. I learned enough in the AMD days to avoid CJS like a plague.)
> You can use async/await in CJS just fine. Sure, sometimes you may need to resort to some ugly async IIFE wrappers because CJS doesn't support top-level await like ESM does, but is that really such a big deal?
It might seem like a small amount of work, but for a library one must to multiply that small amount of work by the number of users who will have to repeat it. It can be a quite large amount in aggregate. And, for what benefit? So I can drop one line from my CI config? It just seems like a huge waste of everyone's time.
Also, as a library user, I would (and occasionally do) get annoyed by seemingly unnecessary work foisted on my by a library author. It makes me consider whether or not I want to actually depend on that library, and sometimes the answer is no.
This is probably where we have the biggest difference in our calculations. I know there's a lot of pain in legacy CJS systems, but from my view (which is maybe more "browser-oriented", which is maybe a bit more Deno/Bun-influences, which comes from a "Typescript-first" mentality going way back to 0.x) it is more legacy "giant balls of mud" maintained by a sparse few developers. I don't see this multiplicand as very big on the scale of library user count. Most CJS for years and years has been transpiled from Typescript or Rollup; most CJS only exists to be eaten by Webpack or other bundler, many of which today rewrite CJS to ESM anyway. From what I see a lot of CJS seems either transpiled out of habit (for some notion of supporting Node < 10 that doesn't make sense with current security support) or by accident (by a misconfigured tsconfig.json, for example, and then often looping back through a transpiler again back to ESM). The way we cut through the Gordian knot of we're all doing too much transpilation to/from CJS is to start eliminating automated transpilation to CJS in the first place. Which is why I find it useful every time to ask people what they are really trying to do when transpiling to .cjs today.
Of course, if your multiplicand is lines-of-code impacted, because I agree there are some great big huge piles of mud in CJS that are likely stuck that way for lack of developers/maintainers and lack of time/budget/money, then worrying about the minority of users still intentionally using CJS is worth caring about, and my sympathies in that situation.
You, of course, know your library's users better than me and maybe you do have a lot of CJS users that I just wouldn't consider in my calculations. I'm not going to stop you from transpiling to CJS if you find that necessary for your library. That's your judgment call. I just wanted to also make sure to ask the questions of "do you really need to?" and "how many users do you actually think it will impact?" out loud. Thanks a lot for the conversation on it, and I'm still going to be a radical banging the "CJS Must Die" drum, but I understand pragmatism and maintenance needs, especially those of legacy applications, and mostly just want to make sure the conversation is an active one and a lot less of passively transpiling stuff that doesn't really need it.
I'm a game developer. I make web games. We run our games with a simple nginx server that simply serves the wasm. We have some JavaScript libraries we use. They have to be raw dog .js.
I don't even know what your "ejs" or "cjs" acronyms mean.
We use the discord JavaScript SDK. Discord only ships it as a node module or as .ts.
It's a pain in our ass to update because we don't know what those tools your talking about are and we don't want to know. Just give me the damn .js
I'd rather kill ESM. And it's not fully supported in Node. It doesn't work in the REPL.
That said with "type": "module" in my own package.json files, I've so far never had a problem importing legacy CJS npm packages into ESM, other than the Types are more likely to be wrong (because packages that only publish CJS are more likely to also not publish their own types) or at least inaccurate for the current import approach (returning only "synthetic default" instead of individual exports, for example). That's a bunch of papercuts having to attempt multiple imports until you understand what shape Node is giving you of that CJS import, but after those papercuts I feel like interop is generally smooth sailing in today's Node.
might have to look into it again but there was an issue where some react/jsx/fragment bit had to be ported to esm first.
(My own .tsx based view library doesn't yet have official SSR support, but I do all my testing in ESM in the built-in Node test runner `node --test` so I don't see any complications in doing .tsx on the "server-side", because that is how I'm testing everything already, I just haven't entirely figured out my "hydration" or "islands" or "stamps" approach so I don't officially support SSR yet. It's on the roadmap and I've made small bits of progress towards it, just need to solve it and haven't had the time/priority.)