Skip to main content

7.10.0 Released: Class Fields in preset-env, '#private in' checks and better React tree-shaking

· 9 min read

We just released a new minor version of Babel!

This 7.10 release includes:

  • Full support for the new Stage 1 proposal, #prop in obj checks for private fields proposal.
  • @babel/preset-env now compiles ES2015-style Unicode escapes (\u{Babe1}) to the equivalent legacy syntax (\uDAAA\uDFE1).
  • Two improvements to the Optional Chaining operator (?.)
  • Parser support for the new Stage 1 Module Attributes proposal (import a from "./a.json" with type: "json").
  • Better tree-shaking support for React code (i.e. React.memo)!
  • Setting up RFCs repo and GitHub Discussions pages!

You can read the whole changelog on GitHub.

Alongside this Babel release, we are releasing the first experimental version of our new polyfills compatibility architecture (see below for more details), thanks to Nicolò and some awesome folks in the community! We began discussions about this over a year ago in a RFC issue within the Babel repository.

As an aside, we now have an official RFC process for discussing changes that significantly impact our users: please check it out over in the babel/rfcs repository! In addition, we've enabled GitHub Discussions on our repository if you have feedback or questions!

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!

New features enabled by default

Parsing for import.meta

Now that it has reached Stage 4, parsing for import.meta is enabled by default, thanks to Kiko. Please note that @babel/preset-env doesn't have any default support for transforming it, because what that object contains is up to the engine and is not defined in the ECMAScript specification.

JavaScript
console.log(import.meta); // { url: "file:///home/user/my-module.js" }

Transforming ​u{...}-style Unicode escapes (#11377)

We also discovered that we didn't have support for compiling a 5-year-old ECMAScript feature: \u{...}-style Unicode escapes! Thanks to Justin, @babel/preset-env can now compile them in strings and identifiers by default.

Example on CodeSandbox

JavaScript
var \u{1d49c} = "\u{Babe1}";
console.log(\u{1d49c});
JavaScript
var _ud835_udc9c = "\uDAAA\uDFE1";
console.log(_ud835_udc9c);

Class Properties and Private Methods to shippedProposals option of @babel/preset-env (#11451)

Lastly, thanks to Jùnliàng we have added @babel/plugin-proposal-class-properties and @babel/plugin-proposal-private-methods to the shippedProposals option of @babel/preset-env. These proposals are not Stage 4 (i.e. part of the ECMAScript standard) yet, but they are already enabled by default in many JavaScript engines.

If you aren't familiar:

JavaScript
class Bork {
// Public Fields
instanceProperty = "bork";
static staticProperty = "babelIsCool";
// Private Fields
#xValue = 0;
a() {
this.#xValue++;
}

// Private methods
get #x() { return this.#xValue; }
set #x(value) {
this.#xValue = value;
}
#clicked() {
this.#x++;
}
}

If you missed it from the last release, in 7.9 we added a new option: "bugfixes": true which can greatly reduce your code output.

JavaScript
{
"presets": [
["@babel/preset-env", {
"targets": { "esmodules": true }, // Use the targets that you was already using
"bugfixes": true // will be default in Babel 8
}]
]
}

Improved optional chaining ?. ergonomics (#10961, #11248)

In TypeScript 3.9, the interaction between non-null assertions (postfix !) and optional chaining has been changed to make it more useful.

foo?.bar!.baz

In TypeScript 3.8 and Babel 7.9, the above would be read as (foo?.bar)!.baz: "If foo is not nullish, get .bar from it. Then trust that foo?.bar is never nullish and always get .bar from it". This means that when foo is nullish that code would always throw, because we are trying to get .baz from undefined.

In TypeScript 3.9 and Babel 7.10, the code behaves similarly to foo?.bar.baz: "If foo is not nullish, get .bar.baz from it and trust me that foo?.bar isn't nullish". Thanks to Bruno for helping to implement this!


Additionally, the class fields proposal recently added support for mixing optional chaining ?. with private fields. This means that the following code is now valid:

JavaScript
obj?.property.#priv;
obj?.#priv;

Note that in the second example, if obj is not nullish and does not have the #priv field, it would still throw an error (exactly as obj.#priv would throw). You can read the next section to see how to avoid it!

Private Fields in in (#11372)

Example on CodeSandbox

JavaScript
class Person {
#name;

hug(other) {
if (#name in other) console.log(`${this.#name} 🤗 ${other.#name}`);
else console.log("It's not a person!")
}
}

This Stage 1 proposal allows you to statically check if a given object has a specific private field.

Private fields have a built-in "brand check": if you try to access them in an object where they aren't defined, it will throw an exception. You can determine if an object has a particular private field by leveraging this behavior with a try/catch statement, but this proposal gives us a more compact and robust syntax to do so.

You can read more about it in the proposal's description and test this proposal by installing the @babel/plugin-proposal-private-property-in-object plugin and adding it to your Babel config. Thanks to Justin for the PR!

Module Attributes parser support (#10962)

The Modules Attributes proposal (Stage 1) allows providing the engine, module loader or bundler some additional information about the imported file. For example, you could explicitly specify that it should be parsed as JSON:

JavaScript
import metadata from "./package.json" with type: "json";

Additionally, they can also be used with dynamic import(). Note the support for trailing commas to make it easier to add or remove the second parameter!

JavaScript
const metadata = await import(
"./package.json",
{ with: { type: "json" } },
);

Thanks to Vivek, Babel now supports parsing these attributes: you can add the @babel/plugin-syntax-module-attributes plugin to your Babel config or, if you are using @babel/parser directly, you can enable the moduleAttributes plugin. Currently, we only accept the type attribute but we might relax this restriction in the future depending on how the proposal evolves.

info

Babel doesn't transform these attributes, and they should be handled directly by your bundler or a custom plugin. Currently babel module transformers ignore these attributes. We are discussing whether we should pass through these attributes in the future.

Better tree-shaking for React components (#11428)

React exposes many pure functions used to annotate or wrap elements, for example React.forwardRef, React.memo or React.lazy. However, minifiers and bundlers aren't aware that these functions are pure and thus they cannot remove them.

Thanks to Devon from the Parcel team, @babel/preset-react now injects /*#__PURE__*/ annotations in those functions calls to mark them as being safe to be tree-shaken away. We had only previously done this with JSX itself (<a></a> => /*#__PURE__*/React.createElement("a", null))

JavaScript
import React from 'react';
const SomeComponent = React.lazy(() => import('./SomeComponent'));
JavaScript
import React from 'react';
const SomeComponent = /*#__PURE__*/React.lazy(() => import('./SomeComponent'));

New experimental polyfills architecture (#10008, babel-polyfills)

In the last three years, @babel/preset-env has helped users reduce bundle sizes by only transpiling the syntax features and including the core-js polyfills needed by their target environments. Currently Babel has three different ways to inject core-js polyfills in the source code:

  • By using @babel/preset-env's useBuiltIns: "entry" option, it is possible to inject polyfills for every ECMAScript functionality not natively supported by the target browsers;
  • By using useBuiltIns: "usage", Babel will only inject polyfills for unsupported ECMAScript features but only if they are actually used in the input souce code;
  • By using @babel/plugin-transform-runtime, Babel will inject ponyfills (which are "pure" and don't pollute the global scope) for every used ECMAScript feature supported by core-js. This is usually used by library authors.

Our position in the JavaScript ecosystem allows us to push these optimizations even further. @babel/plugin-transform-runtime has big advantages for some users over useBuiltIns, but it doesn't consider target environments: it's 2020 and probably very few people need to load an Array.prototype.forEach polyfill.

Additionally, why should we limit the ability to automatically inject only the necessary polyfills to core-js? There are also DOM polyfills, Intl polyfills, and polyfills for a myriad of other web platform APIs. Not everyone wants to use core-js; there are many other valid ECMAScript polyfills which have different tradeoffs (e.g. source size versus spec compliancy), and users should have the ability to use the polyfill of their choice. For example, we are actively working on an es-shims integration.

What if the logic to inject them was not related to the actual data about the available or required polyfills, so that they can be used and developed independently?

We are now releasing the first experimental version of four new packages:

These packages all support a method option for adjusting how they're injected (analogous to what @babel/preset-env and @babel/plugin-transform-runtime currently offer). You can inject a polyfill into an entry point (global scope only) or by direct usage in your code (both global scope and "pure" options). Below is a custom CodeSandbox where you can try out the differences between the polyfill options.

image

We are also releasing @babel/helper-define-polyfill-provider: a new helper package which makes it possible for polyfill authors and users to define their own polyfill provider plugins.

Big thanks to Jordan for working with Nicolò to make it possible to build the es-shims plugin!

tip

If you want to read more about these packages, and learn how to set them up, you can check out the project's README.

caution

These packages are still experimental. We would appreciate feedback about them either on Twitter or on GitHub, but they are not ready for production yet. For example, we still need to wire some polyfills, and we haven't tested the plugins in production applications yet.