hm they have a blog post on converting CJS to ESM but it seems like everything they link to is either unmaintained or I couldn't get it to work https://deno.com/blog/convert-cjs-to-esm
Great article!
It starts to make some order in the JS importing chaos.
I still believe using a build system is the way to go for reasons such as smart code splitting and optimizations, HMR and lots of goodies that come with that (some would say TypeScript 😱).
Again, great article. Thanks!
Thanks for the article, I've always avoided looking into that and use a build tool because, just as you said, it's what most docs will use. It's kinda maddening though to not have access to files on your machine that I feel you should be able to use however? Surely it's a case of https://xkcd.com/927/
my impression is that es modules have really changed that, like there are still a lot of competing things out there but ES modules being an Actual Real Standard really does make a difference
when i was using the "classics" file approach with Vue YESTERDAY, i thought of you and your disbelief in CDNs (also i was about to lose internet..).
took me some time to know that you need a webserver for the es modules 🙃.
I didn't know about the import maps!
As a Python person I often find the JS ecosystem and tooling inscrutable. The CJS/ESM split is still solving itself. Most docs just assume you already know everything.
Sometimes I search around for explanations but I can only find layers of workarounds.
It seems I misread, it's only powered by it. But there is a lot of infrastructure depending on it. For example it's one of the few trusted source of the deno cli:
also curious if anyone knows the answers to my questions in the post, like
- is there a simple way to convert a CommonJS module into an ES module on my computer? (the way esm.js does)
- Is there a tool that automatically generates importmaps for an ES Module that I have downloaded locally?
Nowdays, when I want to convert something, I just send the file to Claude and it converts it right 99% of the times.
I'm also using Cursor as my IDE (with Claude integration), so it's all done directly on my code and applied as a git diff.
This way, I can also understand the transformation.
another question I still have: When people normally build CommonJS modules into “regular” browser JS code, what code is doing that? Obviously there are tools like webpack, rollup, etc, but do those tools all implement their own JS parsers/static analysis? How many JS parsers are there out there?
There’s some commonality in parsing. Webpack, Babel (thus Prettier), and ESLint’s default parsers are all based on Acorn. They generate ESTree-style ASTs, with https://github.com/estree/estree being a collaboration point. I’m not aware of ESTree-based static analysis logic shared between tools, however.
is that true? I did some Googling and my impression was that Babel isn't able to resolve the `require` CommonJS statements, it seems like people are using browserify or esbuild to do that
Ah, sorry, I misinterpreted your question. I'm not sure about the `require`s. I meant Babel translates the code to avoid syntax that might not be supported by older browsers, but I suspect you know that
That category of tools is "bundlers", of which there are seemingly dozens (and more popping up all the time).
I think both Webpack and Rollup depend on the Acorn parser. ESBuild presumably has its own, since it's written in Go. Rust-based tools like SWC and OXC roll their own.
Webpack and Rollup use the Acorn library for parsing. ESBuild rolls its own parsing. SWC is a popular Rust library that includes a parser. The Vite folks are working on a new parser.
From looking into rollup, which iirc used to use a PKG called Acorn, it seems like there might be a shift to a standardized interface called "estree" for parsers to expose.
✋ hi Julia, https://esm.sh creator here. thanks for the great article, love it!
A) in my experience transform cjs to esm is a hell level work, i even created a cjs-module-lexer written in rust(swc), now it works for most packages, but there are still many edge cases i don't konw.
unfortunately i did get any time to make it work locally, i hope to implement it soon.
B) i am currently working on a vscode extension (https://github.com/esm-dev/vscode) that allows you to search npm pakcage & add it to the importmap script, instead of typing it. however it doesn't support local module yet
what about it? i only found https://deno.com/blog/convert-cjs-to-esm but it mostly links to third party and ummaintained projects, I couldn’t get the one maintained project to work
For my personal projects where I try to optimize for learning things outside my everyday frontend job, I love the thrill of importing raw, infrastructure libraries from unpkg and writing everything else in pure HTML/CSS/JS. There’s no better inspiration for creative coding than artificial scarcity!
I might need to test this again, but I believe if you end both the key and value in an importmap with `/`, it will act as a prefix for any subpaths. So, for example, you could just define the `@atcute/client` to point to `index.js`, and then `@atcute/client/` for all further submodule imports.
The headache I ran into with https://esm.sh is that it bundles dependencies by default, which starts out nice, but becomes a rats nest when your packages have common subdependencies (like React, or ThreeJS). You then have to selectively unbundle them with query strings.
I guess I do, and I mistakenly said bundle, I should have said it rewrites the import specifier.
In my case I had an explicit dep on ThreeJS and React, and adding React Three Fiber pulled in a different copy of both by default. Managing this was fine, but a bit fiddly.
Comments
https://deno.com/blog/v2.0
But Deno doesn’t require build tools and greatly simplifies things.
Can handle classic JS, ES modules, and CommonJS with import x from “a url or npm: or node: ”
I found it to be a lot more intuitive - but haven’t grokked it yet with a project
It starts to make some order in the JS importing chaos.
I still believe using a build system is the way to go for reasons such as smart code splitting and optimizations, HMR and lots of goodies that come with that (some would say TypeScript 😱).
Again, great article. Thanks!
took me some time to know that you need a webserver for the es modules 🙃.
I didn't know about the import maps!
As a Python person I often find the JS ecosystem and tooling inscrutable. The CJS/ESM split is still solving itself. Most docs just assume you already know everything.
Sometimes I search around for explanations but I can only find layers of workarounds.
I use it a lot in my @smallweb.run apps.
I'm not too worried it will go away as it is backed by cloudflare. And you can even self-host it: https://github.com/esm-dev/esm.sh/blob/main/HOSTING.md
- is there a simple way to convert a CommonJS module into an ES module on my computer? (the way esm.js does)
- Is there a tool that automatically generates importmaps for an ES Module that I have downloaded locally?
I'm also using Cursor as my IDE (with Claude integration), so it's all done directly on my code and applied as a git diff.
This way, I can also understand the transformation.
I don't know if it does package transformation in a standalone way though
I think both Webpack and Rollup depend on the Acorn parser. ESBuild presumably has its own, since it's written in Go. Rust-based tools like SWC and OXC roll their own.
https://generator.jspm.io/
There's a CLI tool too which might offer support for local packages?
https://jspm.org/docs/jspm-cli/stable/
A) in my experience transform cjs to esm is a hell level work, i even created a cjs-module-lexer written in rust(swc), now it works for most packages, but there are still many edge cases i don't konw.
B) i am currently working on a vscode extension (https://github.com/esm-dev/vscode) that allows you to search npm pakcage & add it to the importmap script, instead of typing it. however it doesn't support local module yet
In my case I had an explicit dep on ThreeJS and React, and adding React Three Fiber pulled in a different copy of both by default. Managing this was fine, but a bit fiddly.