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.