what popular websites use native es-modules (w/o bundling)?

warning - rant against es-modules. will likely get embarrased by well-crafted response, but oh well.

website jslint.com use native es-modules. its peers eslint.org / jshint.com / standardjs.com / prettier.io do not. in fact, haven't found any notable consumer-facing website using native es-modules (admittedly haven't tried too hard searching).

just curious how serious frontend-developers are about ditching bundlers, and adopting native es-modules on the web (or plan to over the next 2-5 years)?

in jslint's case, i would count native es-module as negative, because its more troublesome to use for tool-less cli/scripting tasks in nodejs (vs eslint/jshint which use commonjs). i'm somewhat tempted to follow the other linters and petition jslint revert to commonjs as well.

What aspect of ESM do you find more troublesome than CJS?

I think that because people often want to use a bundler for production, so as to minimize HTTP requests, they therefore opt to bundle as CJS which also happens to give compatibility with significantly older browsers. It also probably doesn't help that Webpack still doesn't allow ESM as an output format (though they are apparently working on that now).

I think it's main use case for live usage is for tool-less demos. It can just work on source, no build steps, always ensuring the latest, rapid development, etc., including in the browser which doesn't support CJS natively.

1 Like

I doubt anyone is ditching bundlers, and I doubt that was ever the intended purpose of es-modules, but as @brettz9 said, it sure is nice to have a native module-loading feature in Javascript without requiring the end-user to use build tools, third-party libraries, or some fragile solution that relies on global state. This was such a pain point for me and many others when I was first learning Javascript. I've got one module and I want to import another from it, how do I do it? There were different solutions, and the path I ended up taking was using the third-party library require.js. But, come on - a third party library to do something as simple as loading a module, can't Javascript do better than this?

You're right that most larger websites will opt for using build tools, and have their compiled artifacts do whatever they want - but their original source could still be using es-modules. (That's certainly the case if you've ever used the create-react-app template - they auto-start you with a setup that uses es-modules in your source).

So in short, es-modules are useful for smaller websites (who aren't using build tools), and within source code before it gets bundled.

Also, eslint does supports es-modules, I've used them together in the past. And I would be surprised if there wasn't a way to configure standardjs or prettier to support es-modules, considering they're both based on eslint.

1 Like

after tweaking, managed to get https://www.jslint.com to respectable mobile pagespeed score with native es-modules and no external tooling.

it helps that jslint was written from ground up as essentially a "bundled" single-file (so there is nothing to bundle).

but this is the exception rather than rule, unless everyone starts writing their web-projects as a single, bundled file.

warning -- reverting back to petty/argumentative self

or some fragile solution that relies on global state.

  • everything in frontend-engineering relies on attaching things to global state. i would argue more tech-debt arises from frontend-developers trying to avoid using window/globalThis than embracing it.

I think it's [es-module] main use case for live usage is for tool-less demos

Also, eslint does supports es-modules

  • i'm talking about hosting their website using native es-modules, instead of as bundled/concatted isomorphic commonjs files like everyone else.

sorry again for my salty replies.

Ah, I thought you were meant that the linting tools didn't support es-modules, not that their websites were or weren't using them, sorry for the confusion.

I'm not really sure what you mean by this. I know the "attach everything to global state" pattern was a common solution before es-modules, and I know this pattern will still get used occasionally today. Certainly, many third-party libraries will still attach their main export to a global value, but that's becoming less and less common as the need to support IE gets weaker, and there's often a way to import these globally-exporting libraries with es-modules instead.

The "attach everything to the global object" solution has a number of issues, such as:

  • name collisions
  • Difficult to track down where imports come from
  • Can't tree shake easily
  • It's difficult for tooling to auto-analyze your imports and exports (tooling includes typescript, your editor, dependency analizers like this one, etc)
  • You often can't pick off the dependencies you need at the top of the file, because those dependencies may not have loaded yet. To be safe, you've got to pick them off within each function that uses them. (by pick dependencies off, I'm referring to doing something like const { MenuBar } = globalThis.myApp.view)
  • etc

You might not find some of these dis-advantages a big deal, but other people certainly do.

Also, what's the advantage of avoiding es-modules and using global state instead? I honestly can't think of any besides IE compatibility (and performance if you're not using bundling, but all major websites use bundling, and you're always going to run slower if you don't use bundling, no matter how you manage your modules). Es-modules are pretty simple, you explicitly export what you want to export, and import what you want to import. You say this format creates a lot of tech-debt - why? Do other languages with proper module systems like Java, python, etc also have similar tech-debt issues too?

Are we talking about the same commonjs, or am I just getting myself confused? commonjs completely avoids global state just like es-modules (except for the addition of the two globals: require() and module). It is also not compatible with browsers (without a build step), because it requires the ability to load modules synchronously. Part of the commonjs standard is the need for a global require() function and a module object (provided by a library or platform such as Node). Searching through the linked jslint website, I couldn't find anywhere in its source code that used require() or module. It was just attaching its whole library onto a global object, which makes sense, it was built in a time when es-modules weren't an option.

Also, concatenation is still a build step, and is a form of bundling - it's just much more primitive and can be error-prone if the concatenation script is hand-built.

As for this argument you originally talked about, I would actually agree with your petition (to at least make the jslint library use commonjs - the website itself can use es-modules, and import a bundled version of the jslin library). As much as I love es-modules, it doesn't seem like node users will ever be able to use es-modules freely, as it will require the entire npm community to switch over to es-modules, and I just don't see that happening any time soon. What I would recommend for anyone building an npm module is to just stick with commonjs and don't use es-modules. If anyone's wanting to to support both node and the browser, than use commonjs and a build step to create a bundled version for browser users. For packages that are only meant to be used in the browser, or if you're literally building a website, use es-modules (preferably with bundling to improve performance).

Others would probably disagree with my stance here, but I just don't have any hope that node users will ever be able to use es-modules and also be able to use everything available on npm. See this article for why I think this way.

1 Like