7.4.0 Released: core-js 3, static private methods and partial application
Today we are releasing Babel 7.4.0!
This release includes support for TypeScript 3.4, the proposal for partial application in function calls, and static private methods.
We added support for meaningful parenthesized expressions in @babel/parser
, and also made it more spec compliant than ever!
Last but not least, both @babel/preset-env
and @babel/transform-runtime
now support core-js@3
, and @babel/template
has some sweet new syntax!
You can read the whole changelog on GitHub.
Special thanks to all the new Babel contributors 😊. Since we started generating release changelogs using GitHub actions we hadn't had the chance to thank them in each release, but since Babel 7.3.3 they have been a lot of people!
- Babel 7.3.3: @Alec321, @mhcgrq, @ilyalesik, @yifei-fu
- Babel 7.3.4: @elevatebart, @ian, @imjacobclark, @irohitb
- Babel 7.4.0: @AmirS, @agoldis, @byara, @echenley, @tanohzana, @matt, @penielse, @pnowak
Many features in this release have been developed in collaboration with our sponsors. Bloomberg has contributed support for a new kind of private element in every release since 7.0 (7.1, 7.2, 7.3), and they've now implemented static private methods! This leaves only static private getters and setters remaining.
Similarly, Trivago (a Base Support Sponsor on OpenCollective) took over the implementation of the partial application plugin.
Over the last month, we have been experimenting with working more directly with companies on various features/optimizations that would benefit the community: RunKit has been sponsoring Nicolò to implement placeholder support in @babel/template
.
When managing a big open source project, not everything is code: we need to manage servers, continuous integration, social media accounts and... a lot of passwords! We really appreciate 1Password for accepting us into their open source support program and providing us a free 1Password Teams account!
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 OpenCollective 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 both fund our efforts in supporting the wide range of JavaScript users and taking ownership of the code. Reach out to Henry at henry@babeljs.io if you'd like to talk more!
core-js
3 (#7646)
We've received lots of kudos for our work on @babel/preset-env
, but much of that should really go to the awesome work done by Denis. He maintains core-js
which provides all the polyfills loaded by @babel/polyfill
, @babel/runtime
and @babel/preset-env
.
core-js@3
was just released, and includes a lot of new features: you can read about them at "core-js@3, babel and a look into the future". Other than all the new proposals, it makes it possible to polyfill transform instance methods using @babel/plugin-transform-runtime
so that they can be used in old browsers without polluting the global environment:
// 'foo' could be either a string or an array, or a custom object
foo.includes("a");
// ⮕
import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";
_includesInstanceProperty(foo).call(foo, "a");
@babel/preset-env
and @babel/plugin-transform-runtime
now support polyfilling proposals: you just need to use corejs: { version: 3, proposals: true }
instead of corejs: 3
in your configuration. Keep in mind that ECMAScript proposals are inherently unstable, and could change in core-js@4
!
Previously, @babel/preset-env
relied entirely on compat-table
data for determining which polyfills needed to be loaded for a particular environment. core-js@3
introduces its own compatibility data set with an exhaustive test suite that should result in much more accurate polyfilling!
Migration from core-js@2
Since versions 2
and 3
of core-js
are incompatible with each other (we don't want to break your code!), it isn't enabled by default.
-
If you are using
@babel/preset-env
, you need to enable thecorejs: 3
option:JavaScriptpresets: [
["@babel/preset-env", {
useBuiltIns: "usage", // or "entry"
corejs: 3,
}]
]Don't forget to update your installed version of
core-js
!npm install --save core-js@3
When using
core-js
3, theuseBuiltIns: "entry"
option not only transformsimport "core-js"
imports, but alsoregenerator-runtime/runtime
and all the nestedcore-js
entry points.For example, when targeting Chrome 72, it will apply this transformation:
Input
JavaScriptimport "core-js/es";
import "core-js/proposals/set-methods";
import "core-js/features/set/map";Output
JavaScriptimport "core-js/modules/es.array.unscopables.flat";
import "core-js/modules/es.array.unscopables.flat-map";
import "core-js/modules/es.object.from-entries";
import "core-js/modules/esnext.set.difference";
import "core-js/modules/esnext.set.intersection";
import "core-js/modules/esnext.set.is-disjoint-from";
import "core-js/modules/esnext.set.is-subset-of";
import "core-js/modules/esnext.set.is-superset-of";
import "core-js/modules/esnext.set.map";
import "core-js/modules/esnext.set.symmetric-difference";
import "core-js/modules/esnext.set.union";Unlike when using
core-js
2, it doesn't transform@babel/polyfill
imports because when used directly that package loads version 2 ofcore-js
. -
If you are using
@babel/plugin-transform-runtime
, you need to use thecorejs: 3
option:JavaScriptplugins: [
["@babel/transform-runtime", {
corejs: 3,
}]
]You can remove
@babel/runtime-corejs2
, but you need to install@babel/runtime-corejs3
!npm remove @babel/runtime-corejs2
npm install --save @babel/runtime-corejs3 -
@babel/polyfill
isn't a plugin or preset, but a runtime package: if we added an option to switch betweencore-js@2
andcore-js@3
, both the package versions would need to be included in your bundle. For this reason, we decided to deprecate it: you now should loadcore-js
for polyfills, andregenerator-runtime/runtime
if you are transforming generators:JavaScript// before
import "@babel/polyfill";
// after
import "core-js/stable";
import "regenerator-runtime/runtime";This gives you the ability to load any version you want, and to update those two package independently.
In case you are interested, you should check the old source of
@babel/polyfill
forcore-js@2
: packages/babel-polyfill/src/index.js.
Partial Application (#9343 and #9474)
This release includes both @babel/parser
and transform support for the partial application proposal, which is currently at Stage 1 (last presented in July 2018). All the implementation work has been done by Behrang Yarahmadi, sponsored by Trivago.
This new feature allows you to bind some arguments and the this
receiver function, similarly to the existing Function#bind
method but with fewer limitations.
const half = divide(?, 2); // half = num => divide(num, 3)
half(6); // 3
element.addEventListener(
"click",
// handleEvent will be called with the correct "this".
this.handleEvent("click", ?) // e => this.handleEvent("click", e)
);
It is also really useful in conjunction with the pipeline operator proposal (especially when using the "minimal" or "F-sharp" variants), since it makes it possible to avoid a lot of arrow functions:
let newScore = player.score
|> add(?, 7)
|> clamp(0, 100, ?);
// Without this proposal:
let newScore = player.score
|> (_ => add(_, 7))
|> (_ => clamp(0, 100, _));
You can test it by adding @babel/plugin-proposal-partial-application
to your config, or by enabling the stage 1
preset in the online repl!
Although the proposal's readme also describes partial application for tagged template literals, it has not been implemented because it will likely be removed.
Static private methods (#9446)
class Person {
static #is(obj) {
return obj instanceof Person;
}
constructor(name) {
if (Person.#is(name)) {
throw "It is already a person!";
}
}
}
Thanks again to Tim (Bloomberg) for implementing this proposal!
If you already use instance private methods you can use this new feature without any additional configuration, otherwise you need to add @babel/plugin-proposal-private-methods
to your plugins list. When using the online repl it is enabled by the stage-3
preset.
Class private features support is only one step away from being complete! 😄
Class Private | Instance | Static |
---|---|---|
Fields class A { #a = 1 } | 7.0.0 | 7.1.0 |
Methods class A { #a() {} } | 7.2.0 | 7.4.0 |
Accessors class A { get #a() {} } | 7.3.0 | ✖️ |
TypeScript 3.4 RC support (#9529 and #9534)
TypeScript 3.4 RC was released a few days ago, and thanks to Tan Li Hau it is already supported by Babel!
There are two new features for type annotations: const
contexts, which mark an object as "deeply frozen", and the readonly
modifier for arrays and tuples.
const student = {
name: "Joe Blogs",
marks: [25, 23, 30]
} as const;
const vowels: readonly string[] = ["a", "e", "i", "o", "u"];
Keep in mind that TypeScript 3.4 RC is not a stable release, so you should wait until TypeScript 3.4 is officially released: you can subscribe to the TypeScript blog to be notified when it will be available. 🙂
Parenthesized expressions (#8025)
Parentheses are not usually meaningful for JavaScript compilers or code generators: they are only "hints" used to tell the parser that some nodes have different precedence from the default one:
Code | 1 + 2 * 3 / 1 + (2 * 3) | (1 + 2) * 3 |
---|---|---|
AST structure |
When the AST has been generated, the precedence of operations is determined by the tree structure and not by the original parentheses: for this reason Babel did not track them.
When printing an AST, @babel/generator
has no knowledge about the original formatting and only generates parentheses where needed.
There are some situations where this causes problems for users. For example, when using Google Closure Compiler, parentheses are used to mark type cast expressions:
/** @type {!MyType} */ (valueExpression)
We already had a ParenthesizedExpression
node to represent parentheses, but it was never generated by @babel/parser
and it could only be injected by custom plugins. Thanks to the work of Erik Arvidsson, you can now use the createParenthesizedExpressions
parser option to automatically track them!
Code | 1 + (2 * 3) | (1 + 2) * 3 |
---|---|---|
AST structure |
@babel/parser
spec compliancy
Daniel is making @babel/parser
more and more compliant to the ECMAScript specification: it is now passing 98.97% of the tests in the Test262 suite. 😎
This release makes @babel/parser
aware of JavaScript's scoping rules: it now knows which variables are declared, if there are conflicts, whether they are hoisted or not, and if a specific syntactic construct is allowed in the context where it is found.
All of these invalid examples are now correctly reported as errors, avoiding the need to manually disallow them in every tool that uses @babel/parser
under the hood:
let a, a; // Duplicate declaration 💥
if (foo) {
if (bar) { var b }
let b; // Duplicate declaration, again 💥
}
export { Foo }; // Error, Foo is not declared ❓
class C {
constructor() {
super(); // Super in a non-derived class 🤔
}
method(d, d) {} // Duplicate parameter 💥
}
Code placeholders (#9364)
Code is not always meant to be directly written by humans: what if some code needs to be generated, maybe using a predefined template?
Template files are often used to generate HTML code, either using a language like PHP or a template engine like Handlebars:
<!-- PHP -->
<section>
<h1><?= $title ?></h1>
<main><?= $body ?></main>
</section>
<!-- Handlebars -->
<section>
<h1>{{ title }}</h1>
<main>{{ body }}</main>
</section>
If you have ever developed a Babel plugin, you might have used @babel/template
: it is a utility which allows you to do the same thing, but generating JavaScript code:
const buildAssign = template`
var NAME = VALUE;
`;
const result = buildAssign({
NAME: varName,
VALUE: varValue,
});
Until now, @babel/template
used uppercase identifier as "placeholders", which then needed to be replaced. While this approach worked well in most cases, it also had some caveats:
- by default, every uppercase identifier was marked as a placeholder and
@babel/template
would threw an error if it was not replaced. - it was not possible to put a placeholder where an identifier is not allowed, for example in place of a function body or an exported declaration.
To solve these problems, we introduced a new syntactic element, which can replace any node: %%placeholder_name%%
.
const buildLazyFn = template`
function %%name%%(%%params%%) {
return function () %%body%%;
}
`;
const result = buildLazyFn({
name: functionName,
params: functionParams,
body: functionBody,
});
This feature has being sponsored by Stripe (via Runkit). We have been experimenting with new ways of sponsoring Babel, and for the first time a company directly sponsored the implementation of a feature by paying a member of the team to work on it. If your company wants to sponsor the implementation of an ECMAScript proposal, or a general improvement to Babel, please contact us!