ES6 Modules, Part 1: Migration Strategy

Owen Densmore
4 min readApr 21, 2017

--

Modules are here! Now what?

I have a simulation project that is bleeding edge modern: all ES6 features and ES6 Modules. The browsers have all caught up with the features, but not Modules. (Update: now most support modules!) Current support:

CanIUse Modules

This is a story of my strategy for dealing with Module migration.

History

Initially I used the brilliant JSPM package manager, then simplified to using just System.js with only Module transpilation as browsers became es6 feature-complete. But this was awkward workflow, requiring a “watch” script always running just to transform all my source file. Sigh.

I should also admit (brag?) that I tossed all workflow task managers like Gulp, Grunt, WebPack and so on .. just npm run scripts instead. God it’s nice to be freed from those shackles!

Then Safari Technology Preview appeared with Module support and I tossed even Babel/SystemJS. Free At Last! Workflow halved! Only workflow was npm install and a few run scripts. Now I simply write code, and run it w/ Safari. Write & Run! Just like in the old days! (You remember those, right?)

But..

I work with a team. I deploy to folks having to use Chrome (which is the only one above with no Module support). Safari is Apple only (but FFox nightly & Edge work too). And I use legacy libraries not yet Module-ified (more on that in a later post).

So I decided on a dual build: native Modules (my src/ dir!) and a Rollup bundle. I chose Rollup because I’m managing libraries, not an application. And it is very simple to use in npm run scripts.

Dual Build

I once tried this before with JSPM: one was interpreted on the fly (the write & run approach) and the other with transpilation. It was hairy, and impossible for teammates to understand. So I was leary of doing it again.

Wrong! Turned out to be so easy I couldn’t believe it! One line in my scripts:

"bundle": "rollup --format iife --name AS src/AS.js > dist/AS.js"

This script converts an “exporter Module”, src/AS.js, into an IIFE, dist/AS.js. A script tag:<script src=”dist/AS.js”></script>creates a global with the name AS. Your basic legacy module.

Exporter

The exporter Module, src/AS.js, imports all my project’s Modules, and exports an object with them in it. I literally used ls -1 *.js initially to create a list of the files, then used my editor to turn it into:

Running the Rollup script on this exporter created the IIFE you’d expect:

var AS = (function () {
'use strict';
const util = { ...
class AgentSet extends Array {...
class Animator { ...
class AscDataSet extends DataSet { ...
...
var AS = {
AgentSet,
Animator,
AscDataSet,
...
util
};
return AS;
}());

I.e. it converts the Module files into a single file with each module being a key in the IIFE object. Codewise, a Module based app uses import, while a script based app plucks the libraries from the AS global bundle:

Formats

We used iife as our Rollup format. But we can use any of amd, cjs, es, iife, umd. Two subtle points:

  • CJS: we can output node’s require() module loader (CommonJS)!
  • ES: WAT? Well, yes, you can Rollup to a Module. Single file with imported, tree-shaken, Modules, exporting the input Modules’ exports. Really quite useful.

CJS Example: I’ve got an Animator class, and it actually would be useful in node:rollup --format cjs src/Animator.js > /tmp/anim produces a CJS format with module.exports = Animator. Here’s a test:

This is so way cool, I’ve finally gotten my browser code to work in node! Yay!

I believe future projects will dual build: both browser based Modules and their browser bundle. But in addition, with the cjs option, also created with a third bundle for node use.

I plan to have a “headless simulator” which runs the simulations without views, simply creating data that can then be fed into analytics libraries, D3, and custom, domain specific viewers. If I’m lucky, I can make it work in both the browser and node. Wow!

Summary

Modules are disruptive. All of us will need strategies for managing migration. This story is how I’m managing my simulation repo:

  • All source files are Modules
  • Write & Run workflow-less development (All but Firefox now.)
  • Distribution workflow using Rollup creating IIFE and node (CJS)

Core Module source files, along with Rollup and it’s tree-shaking, has created a whole new way of managing code. It allows creating sub-Modules (the ES Rollup format option), node require() style modules, yet using modern ES6 Modules and Write & Run development. Code is malleable.

I’m really be interested in other Module migration strategies, I’m sure there will be several. Let me know what you’re doing.

Updates:

Thu May 4 2017: Chrome & Chromium have modules behind a flag.
Wed Aug 23 2017: Now Chrome & Canary no longer need flags, they have native module support.
Sat Nov 25 2017: Now only Firefox requires a flag.
Sat Dec 9 2017: Now my whole team has converted to Modules! So cool!

--

--