Well, that’s a lot of tools to list in a blog post title! We have developed a simple plugin for Karma that adds shims for both ES6 and ES5. This allows you to use cutting edge language features, AND be able to unit test it, right now, even if you’re using AMD!
You can view it at npmjs.com/package/karma-es6-shim. We have also published a sample project that shows how it can be used github.com/radify/karma-es6-shim-example.
So why is this necessary?
I was recently assigned a small project that was written in ES6 and AngularJS that used AMD modules for module source code. It was a tiny proof of concept, which used Babel (AKA 6to5) to transpile the ES6 source code in the src directory into outputted ES5 compatible JavaScript in the build directory.
Even though Babel was used, the code didn’t work in native Chrome without ES6 features switched on. I’d get problems like Object.assign not being a function. Object.assign is not supplied by default with Babel/6to5 (see this link); It’s part of ES6 that is not fully ratified yet - see Mozilla's Object.assign() page, which explains why it wasn’t transpiling in, Therefore, in the HTML, an ES6 shim was supplied:
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.22.1/es6-shim.min.js"></script>
Without this shim, errors would occur; with the shim included, the prototype worked smoothly.
The first thing I wanted to do was to characterise it with a few unit tests before I started developing it further, so I could be confident I wasn’t breaking anything. I installed Karma with its PhantomJS plugin, but I started getting errors like:
PhantomJS 1.9.8 (Mac OS X) ERROR TypeError: 'undefined' is not a function (evaluating 'Function.call.bind(String.prototype.indexOf)')
After quite a lot of googling, I realised that PhantomJS does not support the entire feature set of ES5 (let alone ES6!). Therefore, I included the Karma ES5 shim:
npm install --save karma-es5-shim
Then in my karma.conf.js I added it:
frameworks: ['jasmine', 'requirejs', 'es5-shim'],
Now I was getting the error message:
TypeError: 'undefined' is not a function (evaluating 'Object.assign(this,this._default)')
So, I knew that this meant that I needed the same es6-shim - it was the same error as I got in browser if I removed the es6-shim.
So, what I did is created my own version of karma-es5-shim that also included es6-shim, naming it karma-es6-shim. I installed it:
npm install karma-es6-shim
Then in my karma.conf.js I updated to the new shim:
frameworks: ['jasmine', 'requirejs', 'es6-shim'],
At this point, my tests started passing in Karma/PhantomJS, AND everything in browser is still working. Hooray!
Alternatives to karma-es6-shim
So, is this the right way to add support for incomplete ES6 features? It works for us with our particular combination of tools, but there are other options…
Babel has a polyfill, which provides most of the non-finalised features of ES6, including Object.assign. Feeding Babel the optional: [“runtime”] engages these features. For example:
var pipe = gulp.src(src).pipe(babel({ optional: ["runtime"] }));
It seems, though, that it doesn’t work with RequireJS. From the docs:
The package babel-runtime is required for this transformer. Run npm install babel-runtime --save to add it to your current node/webpack/browserify project. babel-runtime does not support AMD module loaders like RequireJS.
Possibly with some massaging, it could be made to work but I didn’t have any success. Conclusions ES6 is very much a moving target; as we have seen, even features like Object.assign are not yet set in stone. Our karma-es6-shim works for us, for now, to allow us to use same shim in the browser as we use in our unit tests. It’s not as neat as it would be to load the polyfill within Babel, but it’s “good enough” for now!
Roll on widespread ES6 support!
Further reading
- https://github.com/babel/babel/issues/377 Why Babel/6to5 does not support Object.assign out of the box
- https://github.com/michaelbromley/angular-es6 - docs for doing Angular in ES6