CommonJS to ESM in Node.js

With Node 12 and up supporting ES Modules natively and Node 10 — the last version to not support it unflagged — going EOL, it’s time to start migrating your code to ES Modules. Aral Balkan took the time to convert his tool “Place” to embrace them and documented his work:

Yesterday, I refactored Place, which is a non-trivial Node.js app, to use ECMAScript Modules (ESM). Here’s a diff of the full set of changes.

This is the approach I took and some of the issues I ran into, in case it helps someone else

First step: set "type": "module" in your package.json

CommonJS to ESM in Node.js →

Snowpack – build modern web apps without a bundler

Must say I’m quite excited about Snowpack (formerly known as @pika/web, which I covered here) which just got released.

With Snowpack you can build modern web apps (with React, Vue, etc.) without a bundler (like Webpack, Parcel, Rollup). No more waiting for your bundler every time you hit save. Instead, changes are ready in the browser instantly.

You only have to run Snowpack once. By doing so it will generate ES Modules for all of your dependencies, which you can then directly use in the browser.

Example usage:

npm install --save [email protected]
npx snowpack
// index.js
import { h, Component, render } from '/web_modules/preact.js';

// …

Snowpack →

Making Future Interfaces: ES Modules

Once again a highly entertaining video by Heydon Pickering. This time he’s tackling ES Modules: how and where (e.g. in which browsers) can you use them?

💁‍♂️ Even if you already know how to ship ES2015 JavaScript to browsers the video still is worth your time, as it’s very fun to watch!

ESNext: Dynamically import ES modules with “dynamic import()

UPDATE June 2019: This feature has now advanced to Stage-4 and will be part of the ES2020 Specification! 🎉

One of the recent ECMAScript proposals that landed in Chrome 63 is dynamic import():

Dynamic import() introduces a new function-like form of import, which allows one to import on demand. It returns a promise for the module namespace object of the requested module, which is created after fetching, instantiating, and evaluating all of the module’s dependencies, as well as the module itself.

Say you have a lightbox or dialogbox. You could offload its importing to the very moment it is needed:

button.addEventListener('click', async (event) => {
    try {
        const dialogBox = await import('./dialogBox.mjs');…);
    } catch (error) {
        // …

Note that we’re using await here, as it’s better than the typical promise code which uses .then().

The proposal is currently Stage-3

💁‍♂️ Stage-3?

The Technical Committee which is concerned with the standardization of ECMAScript (e.g. TC39) has a 5 stage process in place, ranging from stage-0 to stage-4, by which it develops a new language feature.

Stage-3 is the Candidate Stage where the feature is considered complete and only critical changes will happen based on implementation experience. If all goes well the proposal will advance to Stage 4 without any changes, after which it will to become part of the ECMAScript Specification.

Shipping ES6 ES2015 Modules to Browsers

UPDATE 2017.09.13: Deploying ES2015+ Code in Production Today also provides a good writeup on this subject.

Good workflow fleshed out by Sam Thorogood on using ES2015/ES6 modules with browsers that support, whilst also keeping older browsers (that don’t support it) happy.

The modules

Let’s take these 2 simple modules for example:

// main.js
import {x} from ./foo.js
x().then(y => log(y));
// foo.js
export async function x() {
  let y = await fetch('...');
  y = await y.json();
  return y;

Shipping your code to browsers that do support ES6 Modules

  1. Include your main.js script as a module
    <script type="module" src="main.js"></script>
  2. There is no step 2

Shipping your code to browsers that don’t support ES6 Modules (and thus don’t support ES6)

  1. Use tools – like Rollup – to combine all your ES6 modules into one single file
  2. Transpile the generated module from ES6 to ES5
  3. Include the transpiled script as you’d normally do, but with a nomodule attribute so that the browsers that do support ES6 modules don’t load it (*)
    <script nomodule src="compiled.js"></script>

As both methods exclude each other, use ‘m both and you’re good to go 🙂

(*) As also detailed in Jake Archibald’s great introduction to ECMAScript modules (which you should read) there’s one nasty issue though: Safari 10.1 & Mobile Safari 10.3 don’t support the nomodule attribute. Luckily there’s a workaround to fixing this.

Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)