ECMAScript 2015 (6th edition, commonly referred to as “ES6”) is the current version of the ECMAScript standard, and is a significant update to the language. These features are currently being implemented in major JavaScript engines, but why wait when you can make use of all the awesome today?
I’ve been keen to use many of ES6’s features in my day-to-day development, especially the modules syntax, and destructuring – which is particularly useful in doing away with the option object cruft that JavaScript developers have become used to writing in lieu of native support for named parameters.
Destructuring allows us to take something like this:
… and write it far more succinctly:
This post details how Atlassian account (our single sign-on service) transitioned to using ES6 modules, and what we use to provide a full ES6 environment for development.
ES6 features
Here’s a brief look at the juicy goodness ES6 has to offer, courtesy of Luke Hoban’s excellent ES6 features overview (recommended reading for in-depth descriptions and examples):
arrows
classes
enhanced object literals
template strings
destructuring
default + rest + spread
let + const
iterators + for..of
generators
unicode
modules
module loaders
map + set + weakmap + weakset
proxies
symbols
subclassable built-ins
promises
math + number + string + object APIs
binary and octal literals
reflect api
tail calls
ES6 modules
Up until this point JavaScript has not had native support for modules and so the community has come up with some pretty nice work-arounds, the most popular being Asynchronous Module Definition (AMD) – designed for asynchronous loading of modules and dependencies in the browser – and CommonJS – the most common implementation being in Node.
ES6 modules took the best of both worlds and came up with syntax which should make both camps happy (and lends itself nicely to static analysis to boot).
Just used ES6 module syntax the first time. Very well thought-out. Screams “argued over for many nights by very intelligent people”.
— Dan Abramov (@dan_abramov) March 1, 2015
However, because of the lack of browser support for ES6 modules we still need to compile to something the browsers can understand in the meantime. For us, that meant using AMD – although I’ve been experimenting with SystemJS (and jspm, built on top of it) to potentially do away with the need to convert the modules to AMD.
The environment
The two main ES6 to ES5 transpilers are Babel (formerly named “6to5”) and Google’s Traceur. We chose to use Babel for a few reasons:
Traceur requires a heavyweight runtime, Babel only requires a few optional polyfills
the code generated by Babel is closer to the original source
Babel is a little more feature complete
the web community’s engagement with Babel is massive, and activity is extremely high
Atlassian account’s implementation
Atlassian account is a relatively new code-base, so from the outset we’ve been able to use a modern front-end stack, harnessing all the power that Node and npm provide:
Grunt
Less
AMD via RequireJS
Karma test runner
ESLint
With this toolchain we use Babel via the grunt-babel plugin, transpiling our ES6 modules down to ES5 compatible AMD modules with Babel’s AMD module formatter. The generated code ended up looking more or less like what we already had, but now we were using ES6 modules under the hood!
Grunt + Babel
Using the grunt-babel plugin it was trivial to use a glob expression in the Grunt configuration to specify the ES6 modules we wanted transpiled to ES5 compatible AMD modules.
Polyfill
Babel transpiles most of your code down to being ES5 compatible, but in order to avoid lots of duplication it relies on the core-js polyfill for much of the newer functionality, and a fork of Facebook’s regenerator to provide support for generator functions. Babel bundles this as a single browser-polyfill.js that you include before any of your compiled code. I included it just after the RequireJS config in both our base template and our Karma configuration.
Attempting to include browser-polyfill.js more than once will throw an error, so ensure it only gets included once.
While the default Babel polyfill does an excellent job at polyfilling ECMAScript 6 features, we had a few issues with some of the other things it polyfills:
ECMAScript 5 features, which are unnecessary for the minimum browser versions we support
ECMAScript 7 proposals, which are quite likely to change or be dropped entirely
non-standard language features (e.g. window.setImmediate)
For this reason I built a very simple drop-in replacement for Babel’s polyfill (which still uses core-js and Babel’s regenerator fork), babel-es6-polyfill, that only includes the ES6 polyfills.
Source maps
When developing it’s always nice to be able to debug your un-minified, un-batched source code and source maps provide a convenient way of being able to do that. Thankfully, with Babel this is as trivial as setting the sourceMap option and you’re done. Depending on the folder structure of your project you may also want to set the sourceFileName option, too, so that you don’t have to expand a bunch of folders in the browser’s dev tools to see the original source.
Karma integration
In converting over our tests to ES6 I used the karma-babel-preprocessor. Configuring Karma (via the configuration file, usually named karma.conf.js or similar) to preprocess your tests with Babel before execution is straightforward:
You’ll notice that we’ve enabled source maps for the tests too, which will help if you find the need to debug them.
ESLint
Given that JavaScript is a dynamic and loosely-typed language, it is especially prone to developer error. In order to mitigate this we use the static analysis utility, ESLint, to find problematic patterns or code that breaks certain conventions and guidelines in our codebase.
ESLint allows you to specify environments that define which global variables are predefined and which rules should be on or off by default, as well as individual language options by use of the ecmaFeatures property. To enable a full ES6 environment requires a straightforward configuration:
The future is now
We may not have hover-boards yet, but we can use ES6 today. What are you waiting for? ;)