Fairwell dependancy Hell! Traceur gives you ES6 modules in the browser today.

ES6 modules, Yey!

I’ve been drilling down into the ES6 spec in more detail since last week’s posting to see what juicy pieces of JavaScript goodness I can leverage to improve my next AngularJS project. The feature that jumped out at me instantly is ES6 modules. If you’ve never been down the ‘JavaScript asynchronous script loading’ rabbit hole and you have a spare rainy afternoon to punish your brain, this article is a good starting point: http://www.html5rocks.com/en/tutorials/speed/script-loading/

Apart from JavaScript dependencies being an absolute hell hole that I’ve never encountered in any other language, the fact that it’s near impossible for even the best IDE’s to determine the dependencies for a JavaScript file┬áhas been bugging me for ages. I’m a big fan of Webstorm, but even it has no choice to present the whole JavaScript universe in it’s Intellisense. That makes things a royal pain in the neck in terms of coding productivity. So, ES6 modules to the rescue! can we really say goodbye to our dependency troubles in the browser and IDE? From my current investigations things look really promising.

Transpiling is now a thing!

The main thrust of my last posting was that current browser support for ES6 is sketchy at best. For example, IE10 has next to no support for ES6. Microsoft have rightfully focused their efforts on the new Edge browser. However because Edge is only going to be available in Windows 10 for now, IE11 and it’s descendants are going to be with us for a few years yet. So if you want to use ES6 features in today’s browser landscape, transpiling and browser shims are the only options. Two of the most promising technologies are Babel and Google’s Traceur

With a skim through Traceur’s ‘Getting Started’ page it quickly became evident that it can do in-line transpiling. As I understand it, in-line transpiling is slightly different from your classic shim because it doesn’t modify and extend the core JavaScript namespace, rather it translates your ES6 code to code that will run on ES5. I might be wrong here and I might be in danger of splitting hairs, comments are welcome.

Anyhow, what this means is that you can jump into ES6 without going through the pain of modding your build processes and tool-chain (more on that later). So it’s a great low risk way to dip a toe in the water. Just include the 2 Traceur script files from Google’s CDN in your main HTML page and your up and running!

Leading by Example

Traceur uses the script type ‘module’ to differentiate ES6 code from normal Javascript. Because the browser doesn’t recognize this as a valid script type it will load it but not execute it. This will be done by Traceur once its bootstrapped.

Here’s my example of a simple HTML page that bootstraps Traceur which then transpiles and runs an ES6 script.

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>ES6 modules test with Traceur</title>

</head>
<body>
<h1 id="message1">Loading...</h1>

<h1 id="message2">Loading...</h1>

<h1 id="message3">Loading...</h1>

<!-- Include the Traceur scripts from the google CDN to shim/inline-transpile ES6 -->
<script src="https://google.github.io/traceur-compiler/bin/traceur.js"></script>
<script src="https://google.github.io/traceur-compiler/src/bootstrap.js"></script>
<!-- Traceur uses a special script type for ES6 code - so that the browser doesn't attempt to execute this straight off the bat -->
<script type="module" src="main.js"></script>

</body>
</html>

Pretty Simple eh? Just 3 script tags is all that’s needed for your SPA. The rest of the dependencies will be defined in your JavaScript code and won’t be polluting the global name-space – just like a proper language ;). Now lets look at using ES6 modules with this set up…

Modularity got us to the Moon, but can it tame JavaScript?

So before we look at main.js and see how it loads a module, lets take a look at a noddy little module that I cooked up:

export default function() {

    var element = document.querySelector('#message1');
    element.innerHTML = "hello from my testmodule's default";

};

export function greeter() {

    var element = document.querySelector('#message2');
    element.innerHTML = "hello from my testmodule";

};


export class Greeter {
    constructor(message) {
        this.message = message;
    }

    greet() {
        var element = document.querySelector('#message3');
        element.innerHTML = this.message;
    }
};

Note the use of the export keyword. I’m exporting three things here,:

  1. The default function for this module. This is useful for the popular pattern used frequently in node modules, to export a single entry point for the module
  2. A simple function called ‘greeter’
  3. An ES6 class definition. Just to keep you OO guys happy. I can hear groans from the audience ­čśë

This module is contained in a file called ‘modules/testmodule.js’ relative to the main HTML page. I’ve attached the working source code to the bottom of this post so you can have a play around with it.

Now for main.js:

/* module path is relative to the HTML file that included this script */
/* Note the current traceur release as of 29/06/2015 needs .js appended on to the module path.
 This isn't in line with the final ES6 draft spec which states that the .js should be omitted */

/* import the default export from the test module */
import defaultFn from "modules/testmodule.js";

/* import all module exports from the testmodule into the test module namespace */
import * as testmodule from "modules/testmodule.js";

/* alternately you can import individual exports into this file */
//import {greeter,Greeter} from "modules/testmodule.js"

/* Call the default export from my testmodule */
defaultFn();

/* Call a named function export from my test module */
testmodule.greeter();

/* Instantiate a class export from my test module */
var greeterClass = new testmodule.Greeter("Hello, from the Greeter Class!");
greeterClass.greet();

I think the in-line comments explain things pretty satisfactorily here so I won’t elaborate further as I want to talk about IDE and tooling before you switch off.

Lint us a hand here!

This modularity is all well and good but what are we breaking by using ES6? It turns out that the answer is not that much. Looking at the Chrome developer tools – the scripts are included in the debugging view albeit in their ES5 incarnations. Apparently if we actually do the transpiling in our build processes we get source maps to help between the ES6 -> ES5 translations – but that’s for another day.

I just love using Webstorm, for my day to day hackery and it does not disappoint when it comes to ES6. There’s a setting that allows one to specify the Javascript language version. This quickly solves most problems we’ve caused but if your a user of JSLint you need to switch over to ESLint which supports ES6 syntax and is also supported natively by Webstorm. I’ve included an .eslintrc file in the sample code to get you started. I’m sure users of Sublime text will have similar options available but I can’t be of any help here.

Conclusion

I’ve tested the example code with the current releases of Chrome, Firebox and IE11 and everything seems to run just fine. I would be interested to hear if anyone finds problems with the code, gets it running on other browser versions or any other interesting factoids.

What I would like to do, if time permits, is to get under the hood and see if Traceur is actually loading the scripts asynchronously and can deal with all that crazy complicated edge case stuff around temporal dead zones and the like. Whilst I have the hood up I’d like to profile Traceur running in-line and see if the performance is viable to use without bringing it in to the build process for small Phonegap mobile apps. I’ll post any interesting things I discover on this blog in the near future.

Download Example Code for using ES6 modules with Traceur

EcmaScript 6 – State of the Union

In the last few days I’ve got all excited and hot under the collar that Ecmascript6 (aka JavaScript Harmony) will be going mainstream imminently: http://www.infoworld.com/article/2937716/javascript/its-official-ecmascript-6-is-approved.html

I discovered a great resource that details all the ES6 functionality in a really clean and succinct way:┬á http://es6-features.org/. (I suggest bookmarking this as reading the official spec causes one’s mind to want to shut-down!)

Reading through this, I started to realise that there is some really cool stuff in the specification that is going to make my code a lot more expressive and concise too. Thankfully it’s not just pandering to the old skool OO crowd by adding classes and all that nonsense, which is what I initially feared. Great! I thought, lets go for it – I can use a shim or transpiler to support the current browser releases.

However after a bit of stumbling around on the web it seems like I am jumping the gun somewhat. The go-to source on the web for ES6 browser support is on github at http://kangax.github.io/compat-table/es6/. This fantastic resource by kangax provides a tabular view of the ES6 features broken down against browser versions.

Looking at this table, what first jumps out is the plethora of red ‘unsupported’ cells, which indicates that full native ES6 support is a long way off for all the main stream browsers (I’m looking at you specifically IE!). Even the ES6 shims and transpilers such as Traceur and EchoJs don’t support the full specification yet. So going totally ES6 might not be the most sensible option right now.

On the top of this table are some filter check-boxes, so if you filter out the non stable browsers the picture looks pretty bleak at this time (At time of writing we are on Chome 43 and Firefox┬á 38) and the next few versions in the pipeline don’t look that much better either. However what is surprising is that Microsoft’s new Edge browser is ahead of the game. I’m almost getting excited about Windows 10 in July, almost!.

My conclusion right now is that I need to drill down further into this table to see what chunks of ES6 (or should I now say ES2015?) are available in the transpilers such as babel, core-js and traceur and see if it’s worth going through the pain of pulling one of these into my build processes. I suspect that the ES6 features that have patchy support are probably quite niche and the really useful and cool stuff is solid. I’ll keep you posted on this. I’m also going to look into whether Angular 1.x is any closer to using Object.observe which should really help speed up it’s digest cycles.

As the old proverb/curse goes: “May you live in interesting times!” which certainly rings true in the world of JavaScript at the moment.

EcmaScript 6 is now a thing. What do you think?