If you didn't know already, we're planning on releasing a 7.0 version soon 🙌 ! Work on it actually started back in February, when I just wanted to make a release to drop Node 0.10/0.12 support and remove babel-runtime and various other code. And since then, we've done releases up to alpha.20
.
We're going to update this post throughout the beta releases
Since we're still just a volunteer project, it's been difficult for most of the team to stay focused and motivated to make all these changes and continue to maintain a project that so many companies, bootcamps, and tools rely on so much. But in the meantime we've made a lot of progress: weekly meetings/notes, participating as invited guests at TC39 for the last few meetings, facilitating in both RGSoC and GSoC, and creating an Open Collective.
Upgrading
Upgrading for most projects should be as simple as updating your
package.json
deps to7.0.0-beta.0
. For the whole time we've been working on 7.0, we've been using it in Babel itself (so meta) and at my workplace at Behance.
We will be pinning all dependencies to exact versions until the official release.
{
"devDependencies": {
"babel-cli": "7.0.0-beta.0"
}
}
Specific packages:
Details
babel packages in the monorepo should all be >= 7.0.0-beta.0
babel-preset-env should be at least 2.0.0-beta.0
babel-eslint can be >= 8.0.0
babel-loader should be >= 7.0.0 (out of beta since it uses babel-core as a peerDependency)
grunt-babel can be >= 7.0.0
gulp-babel can be >= 7.0.0
rollup-plugin-babel can be >= 3.0.2
Please check out our upgrade guide and other guide specifically for tool authors which we will also be updating as necessary.
I'd like to go over some notable changes for our first beta release (it's still a lot smaller in scope in terms of breaking changes than the previous 6.0 release).
Re-iterating Project Goals
Before we go into that, I just want to repeat again what the purpose of Babel is.
Since Babel has been renamed from 6to5, browsers have been implementing more of the spec and users are more comfortable with using the latest syntax/build tooling. It shouldn't be surprisingly however that Babel's goals haven't changed much.
Our two goals are hand in hand:
- Help developers transform new syntax into backwards compatible code (abstract browser support away)
- Be a bridge to help TC39 get feedback on new ECMAScript proposals and for the community to have a say in the future of the language.
Thus, I think it's an understatement to say that Babel is a vital part of the JavaScript community (almost 10 million downloads a month of babel-core
) and sorely needs its support. (The only talks I've tried to give are about this point: JSConf EU, React Rally, TC39). I said recently: "What happens if Babel dies"? What happens when the current group of people interested in this project get bored/burned out/move on to other things? (What if it's already happened?). Are we going to do something about it? I don't want to just ask you to help us, you already are us as users of the project.
Ok then, let's talk about some changes!
Drop Support for Unmaintained Node Versions: 0.10, 0.12, 5 (#4315)
Progress in OSS projects often comes at the cost of upgrading for its users. Because of this, we've always been hesitant in making the choice to introduce a major version bump/breaking changes. By dropping unsupported versions of Node, we can not only make a number of improvements to the codebase, but also upgrade dependencies and tools (ESLint, Yarn, Jest, Lerna, etc).
👓 Proposal Updates/Spec Compliancy
AKA the only things most of you care about 😅
Philosophy (Proposals: spec, loose, default behavior)
We've created a new repo: babel/proposals to track our progress on the various TC39 Proposals and meetings.
I also added a section about how we accept new proposals. Our basic thinking is that we will start accepting PRs for anything a TC39 champion is going to present (Stage 0). And we will update them (with your help!) when the spec changes.
Naturally, we will take the opportunity to be as spec compliant as possible (within reasonable speed, etc) as the default behavior. This means if you need a faster/smaller build, you should use the loose
option which will purposely disregard certain spec changes like runtime checks and other edge cases. The reason why it is opt-in is because we expect you should know what you are doing, while others should be able to seamlessly upgrade babel-preset-env
to use the native version of each syntax or stop using Babel entirely and have no issues.
Stage 3: Class Properties (from Stage 2)
babel-plugin-transform-class-properties
: the default behavior is now what was previously the "spec" option, which usesObject.defineProperty
instead of simple assignment.
This currently has the effect of breaking the legacy decorators plugin (which we made the "transform-decorators" plugin in 7.0) if you try to decorate a class property. You'll need to use the
loose
option to be compatible with the version of decorators in the transform until we release the Stage 2 decorators plugin.
Private fields are WIP: #6120
Input
class Bork {
static a = 'foo';
x = 'bar';
}
Output (default)
class Bork {
constructor() {
Object.defineProperty(this, "x", {
configurable: true,
enumerable: true,
writable: true,
value: 'bar'
});
}
};
Object.defineProperty(Bork, "a", {
configurable: true,
enumerable: true,
writable: true,
value: 'foo'
});
Output (loose mode)
class Bork {
constructor() {
this.x = 'bar';
}
};
Bork.a = 'foo';
Stage 3: Object Rest Spread (from Stage 2)
babel-plugin-transform-object-rest-spread
: And now the plugin handles non-string keys (ex: Number/Symbol)
Input
// Rest Properties
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }
// Spread Properties
let n = { x, y, ...z };
console.log(n); // { x: 1, y: 2, a: 3, b: 4 }
Also disallowed
var { ...{ x } } = obj;
var { ...[ y ] } = obj;
Stage 3: Optional Catch Binding (new)
babel-plugin-transform-optional-catch-binding
: allow developers to use try/catch without creating an unused binding.
Input
try {
throw 0;
} catch {
doSomethingWhichDoesNotCareAboutTheValueThrown();
}
Output
try {
throw 0;
} catch (_unused) {
doSomethingWhichDoesNotCareAboutTheValueThrown();
}
Stage 3: Unicode Property Regex (new)
babel-plugin-transform-unicode-property-regex
: compile Unicode property escapes (\p{…}
and\P{…}
) in Unicode regular expressions to ES5 or ES6 that works in today’s environments.
Input
var regex = /\p{ASCII_Hex_Digit}/u;
Output
var regex = /[0-9A-Fa-f]/;
Stage 3: BigInt (new, unfinished)
babel-plugin-transform-bigint
: #6015. This won't be included in the Stage presets because it would be slow to wrap all operators.
Input
50000n + 60n;
Output
import babelCheckBinaryExpressions from "babel-check-binary-expressions";
babelCheckBinaryExpressions(new BigInt("50000"), new BigInt("60"), "+");
Stage 3: Dynamic Import (from Stage 2)
babel-plugin-syntax-dynamic-import
: You only need to parse the syntax since tools like Webpack can handle the transformation in place of Babel. There is also a plugin for Node
Input
const testModule = import('test-module');
Stage 2: import.meta
(syntax only)
A meta property that is only syntactically valid in modules, meant for meta-information about the currently running module provided by the host environment.
Input
const size = import.meta.scriptElement.dataset.size || 300;
Stage 2: Numeric Separators (new)
babel-plugin-transform-numeric-separator
: make numeric literals more readable by creating a visual separation (a_
) between groups of digits.
Input
1_000_000_000
0b1010_0001_1000_0101
0xA0_B0_C0
Output
1000000000
0b1010000110000101
0xA0B0C0
Stage 2: Decorators (from Stage 1), still WIP
Disallowed
// no computed decorator keys
@dec[foo]
class A {}
// no parameter decorators (a separate proposal)
class Foo {
constructor(@foo x) {}
}
// no decorators on object methods
var o = {
@baz
foo() {}
}
// decorator cannot be attached to the export
@foo
export default class {}
Valid
// decorators with a call expression
@foo('bar')
class A {
// decorators on computed methods
@autobind
[method](arg) {}
// decorators on generator functions
@deco
*gen() {}
// decorators with a member expression
@a.b.c(e, f)
m() {}
}
// exported decorator classes
export default @foo class {}
Unsupported (WIP)
// decorated class properties
class A {
@dec name = 0
}
Stage 2: function.sent
(new)
babel-plugin-transform-function-sent
: compile thefunction.sent
meta property, used inside generator functions
Input
function* generator() {
console.log("Sent", function.sent);
console.log("Yield", yield);
}
const iterator = generator();
iterator.next(1); // Logs "Sent 1"
iterator.next(2); // Logs "Yield 2"
Output
let generator = _skipFirstGeneratorNext(function* () {
const _functionSent = yield;
console.log("Sent", _functionSent);
console.log("Yield", yield);
});
Stage 2: export-ns-from
babel-plugin-transform-export-namespace
: a shorthand to import/reexport a namespace. Split out from the oldtransform-export-extensions
which combined this proposal with another
Input
export * as ns from "mod";
Output
import * as ns from "mod";
export {ns};
Stage 1: export-default-from
babel-plugin-transform-export-default
: a shorthand to import/reexport something. Split out from the oldtransform-export-extensions
which combined this proposal with another
Input
export v from "mod";
Output
import _v from "module";
export { _v as v };
Stage 1: Optional Chaining (new)
babel-plugin-transform-optional-chaining
: the operator (?.
) allows you to handle properties of deeply nested objects without worrying about undefined intermediate objects.
Input
a?.b = 42;
Output
var _a;
(_a = a) == null ? void 0 : _a.b = 42;
ES2015: new.target
babel-plugin-transform-new-target
: we never got around to implementingnew.target
support earlier, so now there is a plugin for it that will be included in the ES2015/env presets.
Example
// with a function
function Foo() {
console.log(new.target);
}
Foo(); // => undefined
new Foo(); // => Foo
// with classes
class Foo {
constructor() {
console.log(new.target);
}
}
class Bar extends Foo {
}
new Foo(); // => Foo
new Bar(); // => Bar
Input
class Foo {
constructor() {
new.target;
}
test() {
new.target;
}
}