Transpiling Wars: BabelJS vs Traceur

While browsers play catchup with ES6 support and we wait for older browsers without ES6 support to fizzle out and die, we can actually start using ES6 today thanks to the work of Google’s Traceur or BabelJS.

Whichever you choose for whatever reason, as long as the resulting code is ES5 compatible and your needs have been met, then really there is no competition between any transpiler except for perhaps the resulting code and various parts of the specification that are supported.

As a test, I transpiled code from the new SPA framework Aurelia which allows you to write using ES6 syntax. I ran it both through Tracer and BabelJS to see how things like speed and resulting generated code quality faired. This is by no means an in-depth analysis of the two, just a simple comparison test between output, support and speed.

The import lines in the original example were removed as the compilers would complain that they couldn’t find the files being requested (which is fair enough). I left them there for brevity.

Original code:

import {Router} from ‘aurelia-router’;

export class App { static inject() { return [Router]; }

constructor(router) {
    this.router = router;

    this.router.configure(config => {
           config.title = 'Contacts';
           config.map([
             { route: '', moduleId: 'no-selection',   title: 'Select'},
             { route: 'contacts/:id',  moduleId: 'contact-detail' }
           ]);
    });
}

select(contact) {
    this.selectedId = contact.id;
    this.router.navigate(‘contacts/’ + contact.id);
}

}

Traceur generated code:

$traceurRuntime.ModuleStore.getAnonymousModule(function() { “use strict”; var App = function App(router) { this.router = router; this.router.configure((function(config) { config.title = ‘Contacts’; config.map([{ route: ‘’, moduleId: ’no-selection’, title: ‘Select’ }, { route: ‘contacts/:id’, moduleId: ‘contact-detail’ }]); })); }; ($traceurRuntime.createClass)(App, {select: function(contact) { this.selectedId = contact.id; this.router.navigate(‘contacts/’ + contact.id); }}, {inject: function() { return [Router]; }}); return {get App() { return App; }}; });

BabelJS generated code

var _prototypeProperties = function (child, staticProps, instanceProps) { if (staticProps) Object.defineProperties(child, staticProps); if (instanceProps) Object.defineProperties(child.prototype, instanceProps); };

import {Router} from ‘aurelia-router’;

var App = exports.App = (function () { function App(router) { this.router = router; this.router.configure(function (config) { config.title = “Contacts”; config.map([{ route: “”, moduleId: “no-selection”, title: “Select” }, { route: “contacts/:id”, moduleId: “contact-detail” }]); }); }

_prototypeProperties(App, { inject: { value: function inject() { return [Router]; }, writable: true, enumerable: true, configurable: true } }, { select: { value: function select(contact) { this.selectedId = contact.id; this.router.navigate(“contacts/” + contact.id); }, writable: true, enumerable: true, configurable: true } });

return App; })(); exports.__esModule = true;

More readable code

Winner: BabelJS
While Tracer does an extremely great job at making ES6 code work on older browsers, it also comes at a cost of readability. The output of Traceur generated code looks nowhere near as readable as BabelJS. In-fact, I found sometimes that Traceur takes what should be a pretty simple swap of ES6 methods for similar methods in ES5 and complicates everything ten-fold.

JSX support

Winner: BabelJS
As React.js stays on course, dominating the front-end framework sea and leaving little to no survivors in its wake, the dominance of JSX continues to grow. An alternative JS/HTML like hybrid that is used for writing React.js components

At present, BabelJS is the only transpiler that supports JSX. Perhaps a small thing to consider when choosing a transpiler, but still great nonetheless as developers realise the potential of React.js and ditch traditional full-stack front-end frameworks. If you are working with React, this will probably be a deal-breaker for you.

ES6 specification support

No winner
Both Traceur and BabelJS seem to support all relevant facets of ES6. Now that it is in a code-freeze, there should be no reason that either transpiler supports more of the spec than the other. As pointed out above, BabelJS supports JSX, but that isn’t an ES6 specific thing.

Performance

No winner
Honestly, I found both Traceur and BabelJS to convert my code over to ES5 compatible code pretty quickly. There weren’t any noticeable differences between either when it came to waiting for the resulting code to be translated over.

Conclusion

It doesn’t matter what you choose, both will achieve the same thing. If support for JSX is a requirement for you, then you should go with BabelJS. If resulting code quality is also important to you over function, then BabelJS ticks that box as well.

Either one you choose will work. Both are exceptionally clever transpilers, Google’s Traceur just takes the approach of making your code work in ES5 compatible browsers, BabelJS does the same thing, but the resulting code quality is arguably nicer and easier to decipher/learn from.