Babel 7.14.0 is out!
This release enables class fields and private methods by default (they were promoted to Stage 4 during the recent April TC39 meeting!) and adds brand checks for private fields and static class blocks to @babel/preset-env
's shippedProposals
option.
We added support for Stage 1 async do expressions (using @babel/plugin-proposal-async-do-expressions
), which extends the Stage 1 do expression proposal.
Thanks to Sosuke Suzuki and Pig Fang, Babel can now handle TypeScript 4.3 features. @babel/parser
also has a new option to correctly parse TypeScript declaration files.
Finally, we introduced a new importInterop: node
option to make it easier to produce dual modules by compiling ECMAScript imports to CommonJS that follow Node.js semantics.
You can read the whole changelog on GitHub.
If you or your company want to support Babel and the evolution of JavaScript, but aren't sure how, you can donate to us on our Open Collective and, better yet, work with us on the implementation of new ECMAScript proposals directly! As a volunteer-driven project, we rely on the community's support to fund our efforts in supporting the wide range of JavaScript users. Reach out at team@babeljs.io if you'd like to discuss more!
Highlights
New class features enabled by default
The class fields and private methods proposals just reached Stage 4 and will be officially included in ECMAScript 2022! This was more of a formality since the semantics were already finalized and they've already been implemented in all the major browsers.
You can read more details about this new syntax on MDN (public fields, private fields and methods).
class Check {
static className = "Check"; // static public class field
#value = 3; // # means private!
get #double() { // private getter
return this.#value * 2; // using a private field
}
}
Thus, you can remove @babel/plugin-proposal-class-properties
and @babel/plugin-proposal-private-methods
, since they are now enabled by default in @babel/preset-env
.
Webpack supports this syntax natively as of v5.36.0.
For older versions, a workaround that works with simpler Webpack setups is to manually enable the acorn-stage3
plugin, by installing acorn-stage3
and adding these lines at the beginning of your webpack.config.js
file:
// Require webpack's acorn dependency
const acorn = require(require.resolve("acorn", {
paths: [require.resolve("webpack")]
}));
// Enable the Stage 3 plugin
acorn.Parser = acorn.Parser.extend(require("acorn-stage3"));
If this doesn't work for you, or if you use a different tool that doesn't support class fields, you still need to use the Babel plugins to transform them.
If you are using @babel/preset-env
's shippedProposals
option, it now also includes the @babel/plugin-proposal-private-property-in-object
(introduced in 7.10) and @babel/plugin-proposal-class-static-block
(introduced in 7.12) plugins: you can safely remove them from your configuration.
class Foo {
#bar = "bar";
test(obj) {
return #bar in obj; // private-property-in-object
}
static #x = 42;
static y;
static { // static block
try {
this.y = doSomethingWith(this.#x);
} catch {
this.y = "unknown";
}
}
}
Better ESM-CJS interop
When importing a CommonJS file from an ECMAScript module, Node.js has different semantics than most of the tools in the JavaScript ecosystem.
Suppose that you are depending on the following library:
export default function two() {
return 2;
}
And the author of this library doesn't publish it as-is, but compiles it to CommonJS:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = two;
function two() {
return 2;
}
When importing this library with Babel (or TypeScript, Rollup or similar tools) and compiling your code to CommonJS, it will look like:
import two from "two";
console.log(two());
One day, you decide to provide two versions of your code: a compiled CommonJS one, and one using native ECMAScript modules.
While the compiled version works, the ESM one will throw TypeError: two is not a function
. This is because in Node.js, the default import is not the dependency's exports.default
, but the whole module.exports
object instead.
You could change your code to:
import two from "two";
console.log(two.default());
However, this new code has a problem: it now doesn't work when compiled, because two.default
is not a function.
Babel v7.14.0 adds a new importInterop: "node"
option in the @babel/plugin-transform-modules-commonjs
plugin that allows import
statements to match the native Node.js behavior. You can read more about this option in the docs.
Nicolò from our team also contributed a similar option to @rollup/plugin-commonjs
, which will be out in the next release. Our goal is to help the ecosystem migrate to native ECMAScript modules by providing an easier migration path.
TypeScript 4.3
The new TypeScript version, which will be released as stable in May, supports a few new features:
override
modifiers in class elements- static index signatures (
[key: KeyType]: ValueType
) in classes get
/set
in type declarations
You can read more about them in the TypeScript 4.3 release post. This is supported through @babel/preset-typescript.
async do
expressions
async do
expressions are a Stage 1 proposal built on top of the do
expressions proposal.
They allow using asynchronous blocks within synchronous code, and those blocks are evaluated as a promise:
function sync() {
let x = async do {
let res = await Promise.resolve("Third!");
console.log("Second!");
res;
};
console.log("First!");
x.then(console.log);
}
console.log(sync());
// Logs:
// - "First!"
// - "Second!"
// - "Third!"
You can test this proposal (and report feedback!) by adding the @babel/plugin-proposal-do-expressions
and @babel/plugin-proposal-async-do-expressions
plugins to your Babel configuration.
These proposals are highly experimental. They can, and likely will continue to evolve. It could take years before they are standardized, and may even be rejected altogether. You are welcome to test them, but we do not recommend using them in production.
Do you have any comment or question? Discuss on GitHub!