跳到主要内容

7.4.0 Released: core-js 3, static private methods and partial application

· 阅读需 11 分钟

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!

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:

JavaScript
// '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 the corejs: 3 option:

    JavaScript
    presets: [
    ["@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, the useBuiltIns: "entry" option not only transforms import "core-js" imports, but also regenerator-runtime/runtime and all the nested core-js entry points.

    For example, when targeting Chrome 72, it will apply this transformation:

    Input
    JavaScript
    import "core-js/es";
    import "core-js/proposals/set-methods";
    import "core-js/features/set/map";
    Output
    JavaScript
    import "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 of core-js.

  • If you are using @babel/plugin-transform-runtime, you need to use the corejs: 3 option:

    JavaScript
    plugins: [
    ["@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 between core-js@2 and core-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 load core-js for polyfills, and regenerator-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 for core-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.

JavaScript
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:

JavaScript
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)

JavaScript
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 PrivateInstanceStatic
Fields
class A { #a = 1 }
7.0.07.1.0
Methods
class A { #a() {} }
7.2.07.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.

JavaScript
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:

Code1 + 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:

JavaScript
/** @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!

Code1 + (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:

JavaScript
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:

JavaScript
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%%.

JavaScript
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!


Discuss on Twitter