Skip to content

Commit

Permalink
Create 2019-11-05-7.7.0.md (#2120)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Nov 5, 2019
1 parent 32b741e commit 0cc8a02
Showing 1 changed file with 244 additions and 0 deletions.
244 changes: 244 additions & 0 deletions website/blog/2019-11-05-7.7.0.md
@@ -0,0 +1,244 @@
---
layout: post
title: "7.7.0 Released: Error recovery and TypeScript 3.7"
author: Nicolò Ribaudo
authorURL: https://twitter.com/NicoloRibaudo
date: 2019-11-05 10:00:00
categories: announcements
share_text: "Babel 7.7.0 Released"
---

Today we are releasing Babel 7.7.0!

This release includes new parser features like top-level await (`await x()`, [Stage 3](https://github.com/tc39/proposal-top-level-await)) and Flow `enum` declarations ([Flow proposal](https://github.com/gkz/enums)). And now, `@babel/parser` has the option of recovering from certain syntax errors!

We've also added support for [TypeScript 3.7](https://devblogs.microsoft.com/typescript/announcing-typescript-3-7-rc/): Babel can parse and transform private class fields with type annotations, public class fields annotations defined using the `declare` keyword, type assertion function signatures and template literals in `enum` declarations.

Babel now understands three new configuration files: `babel.config.json`, `babel.config.cjs` and `.babelrc.cjs`, which behave the same as `babel.config.js` and `.babelrc.js` files.

Lastly, Babel 7.7.0 uses 20% less memory than 7.6.0.

You can read the whole changelog [on GitHub](https://github.com/babel/babel/releases/tag/v7.7.0).

<!-- truncate -->

---

Shoutout to [Alejandro Sánchez](https://github.com/alejo90), [Chris Garrett](https://github.com/pzuraq), [彭驰](https://github.com/chris-peng-1244), [Daniel Arthur Gallagher](https://github.com/DanArthurGallagher), [ExE-Boss](https://twitter.com/ExE_Boss), [Eugene Myunster](https://github.com/t0lkman), [Georgii Dolzhykov](https://github.com/thorn0), [Gerald](https://twitter.com/gera2ld), [Linus Unnebäck](https://github.com/LinusU), [Martin Forsgren](https://github.com/dentrado), [Matthew Whitworth](https://github.com/mwhitworth), [Micah Zoltu](https://github.com/MicahZoltu), [Mohammad Ahmadi](https://github.com/m-ahmadi) and [Samuel Kwok](https://github.com/samMeow) for their first PRs!

This release has also been made possible thanks to collaboration with teams of other open source projects: thanks to [Devon Govett](https://twitter.com/devongovett) ([Parcel](https://parceljs.org/)) for implementing support for `babel.config.json` files, and to [George Zahariev](https://twitter.com/gkzahariev) ([Flow](https://flow.org/)) for adding Flow `enum` declarations to `@babel/parser`!

Another special thanks goes to [Bloomberg](https://www.techatbloomberg.com/) for organizing an Open Source Hackaton to encourage their engineers to give back to the community! In particular, [Robin Ricard](https://twitter.com/r_ricard) and [Jaideep Bhoosreddy](https://twitter.com/jbhoosreddy) who are actively working on automating the testing of Babel transforms against the [Test262 suite](https://github.com/tc39/test262).

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](https://opencollective.com/babel) and, better yet, work with us on the implementation of [new ECMAScript proposals](https://github.com/babel/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](mailto:henry@babeljs.io) if you'd like to talk more!


## Highlights

### Top-level `await` parsing ([#10449](https://github.com/babel/babel/pull/10449))

The top-level `await` [proposal](https://github.com/tc39/proposal-top-level-await) allows you to `await` promises in modules as if they were wrapped in a big async function. This is useful, for example, to conditionally load a dependency or to perform app initialization:

```javascript
// Dynamic dependency path
const strings = await import(`./i18n/${navigator.language}.mjs`);

// Resource initialization
const connection = await dbConnector();
```

`@babel/parser` has supported using `await` outside of async functions via the `allowAwaitOutsideFunction` option since version 7.0.0.

Version 7.7.0 introduces a new `topLevelAwait` parser plugin, which has a few key differences:

- It only allows top-level `await` inside modules and not inside scripts, as the proposal mandates. This is needed because synchronous script-based module systems (like CommonJS) cannot support an async dependency.
- It allows to detect the correct `sourceType` when `sourceType: "unambiguous"` is used. Note that, since `await` is a valid identifier in scripts, many constructs which may seem unambiguously modules are actually ambiguous and Babel will parse them as scripts.
For example, `await -1` could either be an await expression which waits for `-1`, or a difference between `await` and `1`.

If you are using `@babel/parser` directly, you can enable the `topLevelAwait` plugin:
```javascript
parser.parse(inputCode, {
plugins: ["topLevelAwait"]
});
```

We also created the `@babel/plugin-syntax-top-level-await` package, which you can add to your Babel configuration:
```javascript
// babel.config.js

module.exports = {
plugins: [
"@babel/plugin-syntax-top-level-await"
]
}
```

Please note that usage of top-level `await` assumes support within your module bundler. Babel itself isn't doing transformations: if you are using Rollup you can enable the [`experimentalTopLevelAwait`](https://rollupjs.org/guide/en/#experimentaltoplevelawait) option, and webpack 5 supports the [`experiments.topLevelAwait`](https://github.com/webpack/webpack/releases/tag/v5.0.0-alpha.15) option.

Starting from this release, `@babel/preset-env` will automatically enable `@babel/plugin-syntax-top-level-await` if the `caller` supports it. *Note*: `babel-loader` and `rollup-plugin-babel` don't yet tell Babel that they support this syntax, but we are working on it with the respective maintainers.

### Parser error recovery ([#10363](https://github.com/babel/babel/pull/10363))

Like many other JavaScript parsers, `@babel/parser` throws an error whenever some invalid syntax is encountered. This behavior works well for Babel, since in order to transform a JavaScript program to another program we must first be sure that the input is valid.

Given Babel's popularity, there are many other tools relying on `@babel/parser`: above all `babel-eslint` and Prettier. For both these tools, a parser which bails out on the first error is suboptimal.

Consider this code, which is invalid because of the duplicated `__proto__` property:

```javascript
let a = {
__proto__: x,
__proto__: y
}

let a = 2;
```

The current workflow with ESLint and Prettier is the following:
1. Prettier can't format the file
1. ESLint reports an `Redefinition of __proto__ property` parser error
1. You remove the second `__proto__` property
1. Prettier can't format the file
1. ESLint reports an `Identifier 'a' has already been declared` error
1. You remove the second `let` keyword
1. Prettier formats the file

Wouldn't it better if it was more like this?
1. Prettier formats the file
1. ESLint reports two errors: `Redefinition of __proto__ property` and `Identifier 'a' has already been declared`
1. You remove the second `__proto__` property and the second `let` keyword

In this release, we are adding a new option to `@babel/parser`: `errorRecovery`. When it is set to true, the resulting AST will have an `errors` property containing all the errors which `@babel/parser` was able to recover from:

```javascript
const input = `
let a = {
__proto__: x,
__proto__: y
}
let a = 2;
`;

parser.parse(input); // Throws "Redefinition of __proto__ property"

const ast = parser.parse(input, { errorRecovery: true });
ast.errors == [
SyntaxError: "Redefinition of __proto__ property",
SyntaxError: "Identifier 'a' has already been declared",
];
```

`@babel/parser` can still throw as not every error is currently recoverable. We'll continue to improve these cases!

### New configuration file extensions ([#10501](https://github.com/babel/babel/pull/10501), [#10599](https://github.com/babel/babel/pull/10599))

Babel 6 only supported a single configuration file: `.babelrc`, whose contents must be specified using JSON.

Babel 7 changed the meaning of `.babelrc`s and introduced two new configuration files: `babel.config.js` and `.babelrc.js` (you can read about the difference between them in the [docs](https://babeljs.io/docs/en/config-files)). We added configuration files with JavaScript to allow defining your own logic when enabling/disabling plugins/options.

However a big benefit of JSON files is easier _cacheability_. The same JavaScript file can produce different values when called two times, while a JSON file is guaranteed to always evaluate to the same object. Also, JSON configurations are easily serializable, while it's not possible to serialize JavaScript values like functions or JavaScript objects with implicit data or relationships.

Note that Babel also caches transforms when using JavaScript-based configurations, but the config file must be evaluated (in order to know if the cache is still valid) and the cache [manually configured](https://babeljs.io/docs/en/config-files#apicache).

For these reasons, Babel 7.7.0 introduces support for a new configuration file: `babel.config.json`, whose behavior is the same as `babel.config.js`.

We also added support for two different configuration files: `babel.config.cjs` and `.babelrc.cjs`, which must be used when using node's [`"type": "module"`](https://nodejs.org/api/esm.html#esm_code_package_json_code_code_type_code_field) option in `package.json` (because Babel doesn't support ECMAScript modules in config files). Apart from this `"type": "module"` difference, they behave exactly like `babel.config.js` and `.babelrc.js`.

### TypeScript 3.7 ([#10543](https://github.com/babel/babel/pull/10543), [#10545](https://github.com/babel/babel/pull/10545))

[TypeScript 3.7 RC](https://devblogs.microsoft.com/typescript/announcing-typescript-3-7-rc) includes support for optional chaining, nullish coalescing operator, assertion functions, type-only field declarations and many more type-related features.

Optional chaining (`a?.b`) and nullish coalescing (`a ?? b`) have been supported in Babel since 7.0.0 via `@babel/plugin-proposal-optional-chaining` and `@babel/plugin-proposal-nullish-coalescing-operator`.

In Babel 7.7.0, you can now use assertion functions and `declare` in class fields:

```typescript
function assertString(x): assert x is string {
if (typeof x !== "string") throw new Error("It must be a string!");
}

class Developer extends Person {
declare usingBabel: boolean;
}
```

To avoid breaking changes, we introduced support for `declare` in class fields behind a flag: `"allowDeclareFields"`, supported by both `@babel/plugin-transform-typescript` and `@babel/preset-typescript`. This will likely become default behavior, so it is recommended that you migrate your config to use it:

```jsonld
{
"presets": [
["@babel/preset-typescript", {
"allowDeclareFields": true
}]
]
}
```

### Use object spread in compiled JSX ([#10572](https://github.com/babel/babel/pull/10572))

When using spread properties in JSX elements, Babel injects a runtime helper by default:

```jsx
<a x {...y} />

// 🡇 🡇 🡇

function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }

React.createElement("a", _extends({
x: true
}, y));
```

In 2016, as support for native ES6 improved, we added the `useBuiltIns` option to `@babel/plugin-transform-react-jsx` which allowed the compiled output to directly use `Object.assign` and removed excess code:

```jsx
<a x {...y} />

// 🡇 🡇 🡇

React.createElement("a", Object.assign({
x: true
}, y));
```

However given the native support for object spread, it allows us to produce even more optimized code:

```jsx
<a x {...y} />

// 🡇 🡇 🡇

React.createElement("a", { x: true, ...y });
```

You can enable it using the `useSpread` option with either `@babel/preset-react` or `@babel/plugin-transform-react-jsx`:

```jsonld
{
presets: [
["@babel/react", { useSpread: true }]
]
}
```

### Memory usage improvements ([#10480](https://github.com/babel/babel/pull/10480))

Since the beginning, we have been making efforts ([#433](https://github.com/babel/babel/pull/433), [#3475](https://github.com/babel/babel/pull/3475), [#7028](https://github.com/babel/babel/pull/7028), etc.) to improve performance. Babel 7.7.0 now uses 20% less memory, and transforms large files 8% faster when compared to 7.6.0.

In order to achieve these results, we optimized different operations done during the lifetime `NodePath` objects (used to wrap every AST node):

1. We now avoid initializing some rarely used object properties until they are needed, allowing us to avoid an `Object.create(null)` allocation for almost every AST node.

1. We reduced bookkeeping workload for every single node visit, by replacing a few uncommon properties with getters so that `@babel/traverse` can skip updating them.

1. We optimized memory usage by compressing several boolean properties used to represent the status of a node traversal (i.e. skipped, stopped or removed) into a [bit array](https://en.wikipedia.org/wiki/Bit_array).

All these improvements add up to the following difference in transform performance and memory usage:

| Performance | Memory usage |
|:-----------:|:------------:|
| ![](https://i.imgur.com/bs3DoOX.png) | ![](https://i.imgur.com/n1bQSj1.png) |

You can also checkout the [raw data](https://docs.google.com/spreadsheets/d/1lFUKviJwJP83SulEG7tHoaHO4bvxPt1u2UrEySrPAXc/edit?usp=sharing) of the charts above. If you want to read more about this topic, you can read [Jùnliàng's detailed write-up](https://hackmd.io/@VQ5Lhi9URomRC0KamZdyvw/ryuUaFGwS) about the changes he made to get those improvements!

0 comments on commit 0cc8a02

Please sign in to comment.