5.0.0 Released

Posted Mar 31, 2015 by Sebastian McKenzie

In the past few months Babel has been welcomed into several major communities such as Node, React, Ember, Backbone, Angular, Rails, and many others. We launched the Users page only a few weeks ago and it's really cool to see everyone that is using it. Companies like CloudFlare, Netflix, Mozilla, and Yahoo!. Projects like Ghost, Atom, Mapbox, and so many more.

We've seen tons of blog posts, talks, events, courses all about ES6+ using Babel, and official Babel tools have been downloaded nearly 2 million times.

Today we are making by far the largest release of Babel ever.

If you're upgrading from Babel 4.x please see the breaking changes.

This release includes the new ES7 proposals:

The entire internal traversal and transformation pipeline has undergone a rewrite that substantially increases flexibility and will allow many future pipeline performance optimisations.

This release also brings a plugin API, this allows consumers to plug in their own custom transformers to utilise the powerful transformation mechanisms that Babel has to offer.

You can view the complete CHANGELOG here.

And as usual if you run into any regressions please report them immediately.

TC39 Process

In this release you'll start to see us aligned with the TC39 process. The TC39 is the technical committee from ECMA that writes the ECMAScript standard. Their process is categorised into 5 stages:

  • Stage 0 - Strawman
  • Stage 1 - Proposal
  • Stage 2 - Draft
  • Stage 3 - Candidate
  • Stage 4 - Finished

Proposals that are stage 2 or above are enabled in Babel by default. Now this does not mean that they're guaranteed to be included in future ECMAScript specifications or even Babel itself. Stage 2 is considered a good point for inclusion by default in Babel due to their relative maturity and need for critical proposal feedback.

Now let's dive into the changes we made to 5.0.


Contents:

New Features

New Proposals

Stage 0: Class Properties

Jeff Morrison's stage 0 class property initializers proposal fills the void of property composition on classes. These are analogous with the class properties example listed in the React 0.13 beta announcement.

Example:

class Person {
  firstName = "Sebastian";
  static lastName = "McKenzie";
}

assert(new Person().firstName, "Sebastian");
assert(Person.lastName, "McKenzie");

Usage:

require("babel").transform("code", {
  optional: ["es7.classProperties"]
});
// or
require("babel").transform("code", { stage: 0 });
$ babel --optional es7.classProperties script.js
# or
$ babel --stage 0 script.js

Stage 1: Decorators

Yehuda Katz' stage 1 decorators proposal allows you to elegantly compose property descriptors and metadata decoration. In the future this will allow the powerful Ember Object Model to easily be represented with native classes.

Example:

function concat(...args) {
  let sep = args.pop();

  return function(target, key, descriptor) {
    descriptor.initializer = function() {
      return args.map(arg => this[arg]).join(sep);
    }
  }
}

function autobind(target, key, descriptor) {
  var fn = descriptor.value;
  delete descriptor.value;
  delete descriptor.writable;
  descriptor.get = function () {
    var bound = fn.bind(this);
    Object.defineProperty(this, key, {
      configurable: true,
      writable: true,
      value: bound
    });
    return bound;
  };
}

class Person {
  firstName = "Sebastian";
  lastName = "McKenzie";

  @concat("firstName", "lastName", " ") fullName;
  @concat("lastName", "firstName", ", ") formalName;

  @autobind
  getFullName() {
    return `${this.firstName} ${this.lastName}`;
  }
}

assert(new Person().fullName, "Sebastian McKenzie");
assert(new Person().formalName, "McKenzie, Sebastian");
assert(new Person().getFullName.call(null), "Sebastian McKenzie");

Usage:

require("babel").transform("code", {
  optional: ["es7.decorators"]
});
// or
require("babel").transform("code", { stage: 1 });
$ babel --optional es7.decorators script.js
# or
$ babel --stage 1 script.js

Stage 1: Export Extensions

Lee Byron's stage 1 additional export-from statements proposal completes the symmetry between import and export statement, allowing you to easily export namespaces and defaults from external modules without modifying the local scope.

Exporting a default

export foo from "bar";

equivalent to:

import _foo from "bar";
export { _foo as foo };

Exporting a namespace

export * as ns from "mod";

equivalent to:

import * as _ns from "mod";
export { _ns as ns };

Usage:

require("babel").transform("code", {
  optional: ["es7.exportExtensions"]
});
// or
require("babel").transform("code", { stage: 1 });
$ babel --optional es7.exportExtensions script.js
# or
$ babel --stage 1 script.js

React Optimisations

In preparation for React 0.14, Babel supports some optimisation transformers for JSX.

Constant Elements

Starting with 0.14 ReactElements and their props objects can be treated as as value types. i.e. any instance is conceptually equivalent if all their values are the same.

Take this function for example:

import React from "react";

function render() {
  return <div className="foo" />;
}

This can be optimized by moving the JSX out of the function body so that each time it is called the same instance is returned:

import React from "react";

var _ref = <div className="foo" />;

function render() {
  return _ref;
}

Not only does it allow us to reuse the same objects, React will automatically bail out any reconciliation of constant components - without a manual shouldComponentUpdate.

Usage:

require("babel").transform("code", {
  optional: ["optimisation.react.constantElements"]
});
$ babel --optional optimisation.react.constantElements script.js

Inline Elements

Production only

Inline Elements should only be enabled in production as multiple React warning messages are suppressed which is extremely risky in development.

Starting with React 0.14 ReactElements can be inlined:

<div className="foo">{bar}<Baz key="baz" /></div>

as objects:

{ type: 'div', props: { className: 'foo', children:
  [ bar, { type: Baz, props: { }, key: 'baz', ref: null } ]
}, key: null, ref: null }

This improves performance over the existing React.createElement call by inlining the result of it.

Usage:

require("babel").transform("code", {
  optional: ["optimisation.react.inlineElements"]
});
$ babel --optional optimisation.react.inlineElements script.js

.babelrc

Babel 5.0.0 has support for .babelrc out of the box across its entire range of integrations. This means that it will work across babel/register, babel-node as well as across the entire range of build system plugins and module loaders such as babel-loader, babelify, and others.

.babelrc is equivalent to JSHint's .jshintrc and JSCS' .jscsrc.

{
  "stage": 1,
  "ignore": [
    "foo.js",
    "bar/**/*.js"
  ]
}

See the docs for more info.

Plugin API

5.0.0 also introduces the long anticipated plugin API. This allows you to hook into the powerful traversal and transformation internals of Babel. See the docs for more info.

Breaking Changes

Experimental Option

The experimental option has been removed. Fear not though, there is a replacement. Babel now categories the ES7 transformers by TC39 stages.

tl;dr If you're using the experimental option, simply change it to $ babel --stage 0 or { stage: 0 }.

Reminder: Proposals that are stage 2 or above are enabled by default.

Stage 0

  • es7.classProperties
  • es7.comprehensions

Stage 1

  • es7.asyncFunctions
  • es7.decorators
  • es7.exportExtensions
  • es7.objectRestSpread

Stage 2 (Stage 2 and above are enabled by default)

  • es7.exponentiationOperator

For a list of all current ES7 proposals please see the tc39/ecma262 repo.

returnUsedHelpers option

The returnUsedHelpers option has been renamed to metadataUsedHelpers and the returning result object has been changed from usedHelpers to metadata.usedHelpers.

Class Changes

5.0.0 introduces some updated derived class semantics that are long overdue.

super() must be called in a derived class constructor.

class Foo extends Bar {
  constructor() {
    // no `super();`
  }
}

Access to this before super() in a derived class constructor is not allowed.

class Foo extends Bar {
  constructor() {
    this.foo; // `this` access before `super();`
    super();
  }
}

super() is only allowed in derived class constructors.

class Foo {
  constructor() {
    super(); // not in a derived constructor
  }
}

Removed Features

  • The playground has been removed so development can be focussed on mainstream ES features and proposals. This also reduces the risk of syntactic conflicts preventing certain official features from being implemented.
  • Abstract references have been removed as the proposal has been superseded. Support for one or more of the superseding proposals may be implemented in the future.

In closing, we hope that you are now as excited about this release as we are. There's a lot that went into it, and we believe this will set us up for a long time into the future.

— The Babel team

Imports are now hoisted

In 4.x, imports were inlined as where they appeared in the code. Which means that this code:

global.test = 'test'
import './test'

would compile to:

'use strict';

global.test = 'test';
require('./test');

However, from 5.x on, this behaviour has been changed in order to comply with the ES6 spec and imports will now be hoisted. What this means in practical code is that the snippet above will get converted to something like:

'use strict';

require('./test');
global.test = 'test';

If your code required certain bits and pieces to be executed in between a specific module being imported -which might be the case while testing code and you need to fake some window properties :)- you may want to extract that away into its own file and import it before the code that needs it.