I've started using ESM in a static website builder too [1]. It works, kinda.<p>Problems that I encountered:<p>To get `__dirname`, you have to do an awkward workaround using path and url [3].<p>For npm modules that implement a path e.g. `@lib/method`, the import of this currently isn't allowed in node without a flag.<p>For ESM incompatible modules, a createRequire [2] function is available to get the old `require` syntax.<p>For import statements, the cache cannot be deleted yet [4].<p>Overall, I'm please to see that nodejs will soon be ESM compatible. All changes that have been made look reasonable and useful to me.<p>In fact, I now often get the pleasurable "wow that makes sense" feeling when using node. It has has great docs, nice new features like worker_threads and it's productive for me. I'm a fan!<p>1: <a href="https://github.com/TimDaub/kloi" rel="nofollow">https://github.com/TimDaub/kloi</a><p>2: <a href="https://nodejs.org/api/module.html#module_module_createrequire_filename" rel="nofollow">https://nodejs.org/api/module.html#module_module_createrequi...</a><p>3: <a href="https://stackoverflow.com/questions/46745014/alternative-for-dirname-in-node-when-using-the-experimental-modules-flag" rel="nofollow">https://stackoverflow.com/questions/46745014/alternative-for...</a><p>4: <a href="https://github.com/nodejs/modules/issues/307" rel="nofollow">https://github.com/nodejs/modules/issues/307</a>
I find using ES Modules in Node.js to be full of gotchas and anti-patterns - sticking with CommonJS seems more functional in most cases.<p>1: With ES Modules, imports are asynchronous.<p>Synchronous imports are incredibly powerful. You can ensure that your application doesn't block on first requests, but instead on load. Initializing connection pools, setting up caches, etc.<p>You'll also find quite a few packages in NPM that explicitly depend on require over import just so that they can perform the bindings they have to before things get going (see <a href="https://www.npmjs.com/package/honeycomb-beeline" rel="nofollow">https://www.npmjs.com/package/honeycomb-beeline</a> as an example).<p>2: With ES Modules imports are commonly destructured.<p>I know it makes your code feel lighter to have const { add } = import("whatever"); - but as your files grow larger, and those imports start to become complicated bits for middleware or other features, you're just making it more difficult for future maintainers to figure out what those functions mean. A bit of context never hurt anybody, and for my money I'll be that this:<p>app.use(check);<p>is not nearly as useful as<p>app.use(Validator.check);<p>The example may be contrived - but 9/10 times I find this makes code better than the alternative.<p>All of these arguments go out the door for Frontend work - that's a totally different terrain. I think one of the big arguments is that developers want to use the exact same JavaScript on frontend and Node applications - but that's an argument unwilling to admit the fact that they're two very different tasks. :)
In my opinion and recent experience working with ESM, the design and tradeoffs are worth the trouble <i>especially</i> if you use a bundler or an optimizing build step, and especially if you prefer named imports/exports.<p>On the other hand, benefits of ESM on Node are currently conditional on whether you’re running the same code on the browser and want consistent behavior on the server. For server-only projects I honestly think you’re still better off using a build step that compiles to CommonJS. That may change as Deno grows in popularity and interoperability becomes more of a concern, or as other compelling ESM features are introduced (for instance native import maps).
I went down the ESM rabbit hole, after 2 months I decided to stay away. Main reason is/was I want true monorepos in TypeScript with single DRY type definitions (so you need to import them to the FE). This alone is with CommonJS already a bigger endeavor but impossible when mixing in ESM for the frontend parts. Nobody should be blamed here because it's so much legacy, so many systems involved, so yeah.
As far as I know, ES modules do not allow variables as module name:<p><pre><code> const moduleName = "foo"
import something from moduleName // will not work
const something = await import(moduleName) // will not work
</code></pre>
I'm genuinely curious about how someone would do this with ES modules:<p><pre><code> // somewhere in a function or a class method
const plugins = pluginModules.map(require)
</code></pre>
EDIT: I was wrong, the import() function does work with variables.
ESM modules are literally only downsides and workarounds, there is absolutely no user benefit at the moment, why would anyone adopt this technology other than a standards board saying "You Should Do It"