Skip to main content

7.9.0 Released: Smaller preset-env output, Typescript 3.8 support and a new JSX transform

· 9 min read

While preparing the Babel 8.0.0 release (in the next months), we just finished working on a new minor release which includes updates related to @babel/preset-env, TypeScript 3.8, Flow, and JSX!

A few months ago, Jason Miller started working on @babel/preset-modules: an experiment to greatly reduce bundle sizes when using the module/nomodule pattern. We are excited to announce that its functionality has now been merged into @babel/preset-env! This means that its optimizations can be applied to all preset-env targets values, without a separate preset.

Note: These optimizations will be enabled by default in Babel 8. They can be manually enabled in Babel 7.9 by passing in the option { bugfixes: true } to preset-env.

This release also has full support for TypeScript 3.8, which introduced explicit type-only imports and exports (i.e. export type { foo }), and for Flow 0.120, which introduced the declare modifier for class fields (i.e. class C { declare foo: string }).

We also worked with the React team to provide a new JSX transform, which will make it possible for React and React-like libraries to further optimize the creation of JSX elements with the addition of the jsx function vs. React.createElement.

Lastly, @babel/parser now supports an additional ECMAScript proposal: Record & Tuple. Please note that this is only parser support, and the transforms are still being worked on.

You can read the whole changelog on GitHub.


Special thanks go to Luna Ruan from the React Team (Facebook) for contributing the new JSX transform, and Rick Button (Bloomberg) who implemented parser support for the Record & Tuple proposal!

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!

@babel/preset-env's bugfixes option (#11083)

The new bugfixes option in @babel/preset-env is the successor to using @babel/preset-modules directly.

If you want more context about the issues that this change helps with, we'd suggest you listen to (or read) the recently published podcast episodes with Jason: #2 Modern JavaScript and the Future of preset-env and #3 Compiling Your Dependencies.

Until now, @babel/preset-env (and Babel plugins in general) grouped ECMAScript syntax features into collections of closely related smaller features. These groups can be large and include a lot of edge cases. For example, the "function arguments" group and plugin includes destructured, default and rest parameters.

From this grouping information, Babel enables or disables each group based on the browser support target you specify to @babel/preset-env’s targets option.

Here's the problem: if any version of any browser in that list contains a bug triggered by modern syntax, the only solution (that we considered at the time) was to enable the corresponding transform group that fixes that bug.

Over time more bugs would eventually be uncovered and reported to our issues, which caused preset-env to output more code for the sake of these edge cases. In the worst case, it meant that the output was the same as just compiling everything to ES5, which preset-env was created to help prevent.

When the bugfixes: true option is enabled, @babel/preset-env takes a different approach: it transpiles the broken syntax to the closest non-broken modern syntax.

For example: all of the new syntax features relating to function parameters are grouped into the same Babel plugin (@babel/plugin-transform-function-parameters). When targeting edge 16, it has a bug related to parsing shorthand destructured parameters with default values within arrow functions:

JavaScript
// this breaks in Edge 16:
const foo = ({ a = 1 }) => {};

// .. but this doesn't:
function foo({ a = 1, b }, ...args) {}

// ... and neither does this:
const foo = ({ a: a = 1 }) => {};

This means that if we give @babel/preset-env this input and targeted Edge 16:

JavaScript
const foo = ({ a = 1 }, b = 2, ...args) => [a, b, args];

It transforms it down to ES5-like parameters:

JavaScript
const foo = function foo(_ref, b) {
let { a = 1 } = _ref;

if (b === void 0) { b = 2; }

for (
var _len = arguments.length,
args = new Array(_len > 2 ? _len - 2 : 0),
_key = 2; _key < _len; _key++
) {
args[_key - 2] = arguments[_key];
}

return [a, b, args];
};

However, if we enable the bugfixes option, it only transforms the broken syntax:

JavaScript
const foo = ({ a: a = 1 }, b = 2, ...args) => [a, b, args];

You can see this example in action at our REPL

You can enable this option today by adding it to @babel/preset-env in your configuration:

{
"presets": [
["@babel/preset-env", {
"targets": { "esmodules": true }, // Use the targets that you was already using
"bugfixes": true
}]
]
}
tip

Currently, the bugfixes option gives the best results when using the esmodules: true target, which allows you to target the browsers with native ES modules support and use the module/nomodule pattern. We hope to continue improving it over the next few releases, and enable it by default in Babel 8.

Moving forward, we would like to work with the community (including browsers) to allow for this kind of approach to work smoothly as we continually transition in JavaScript's development. In the ideal scenario, Babel would be able to implement and help influence the future of new proposals as they are suggested and refined, and smooth over these edge cases for existing standards so that the minimum compiled output is possible for all users of JavaScript based on their targets.

TypeScript 3.8: type-only imports and exports (#11171)

You can now explicitly mark imports and exports as type-only, similarly to what you can already do in Flow:

import type { SomeThing } from "./some-module.js";

export type { SomeThing };

By doing so, Babel can safely decide which imports or exports are used for types and which are values.

Since Babel doesn't analyze types and works on a per-file basis (similarly to TypeScript's --isolatedModules option), until now @babel/plugin-transform-typescript handled imports not used as values as if they were type-only.

Starting from Babel 7.9 you can use the new type modifier without any configuration change.

We recommend configuring @babel/preset-typescript or @babel/plugin-transform-typescript so that it only considers imports as type-only when there is the explicit type keyword, similarly to TypeScript's --importsNotUsedAsValues preserve option:

babel.config.json
{
"presets": [
["@babel/preset-typescript", {
"onlyRemoveTypeImports": true
}]
]
}
tip

These features were contributed by the Babel team together, and by Siddhant N Trivedi. If you have interested in seeing how it's all done, please check how we did it on YouTube!

Flow declare fields (#11178)

The class fields proposal specifies uninitialized class fields are initialized to undefined: this is different from what Babel does with Flow, because it simply ignores them.

For this reason, the Flow team has added support for the declare modfier for class fields:

JavaScript
class Foo {
x: ?string; // A "real" field
declare y: number; // A type-only field
}

In the above example, only y should be completely removed by Babel.

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

babel.config.json
{
"presets": [
["@babel/preset-flow", {
"allowDeclareFields": true
}]
]
}

A new JSX transform (#11154)

The React team created an RFC back in February of last year to discuss simplifying element creation.

In a future stable release, React will support a group of new functions for instantiating JSX elements as an alternative to the legacy general-purpose React.createElement function. This will allow optimizing them better in the future.

tip

While it's not released in a stable release yet, you can try it out on the experimental React release channel:

Shell
npm install react@experimental react-dom@experimental

We worked with the team to finish a new transform that supports compiling JSX to these new functions. It also automatically imports "react" (or other libraries which support the new API) when needed, so you don't have to manually include it anymore.

As an example, this input:

JSX
function Foo() {
return <div />;
}

would turn into:

JSX
import { jsx as _jsx } from "react/jsx-runtime";
function Foo() {
return _jsx("div", ...);
}

Note: The functions inside react/jsx-runtime and react/jsx-dev-runtime are not meant to be used outside the @babel/plugin-transform-react-jsx and @babel/plugin-transform-react-jsx-development plugins themselves.

In summary (and please check the RFC for more information), the transform will:

  • Always pass children as props.
  • Pass key separately from other props.
  • In DEV,
    • Pass a flag determining if it was static or not.
    • Pass __source and __self separately from other props.

Usage: You can enable this new transform by passing { "runtime": "automatic" } (as opposed to "classic") to @babel/preset-react (or @babel/plugin-transform-react-jsx):

babel.config.json
{
"presets": [
["@babel/preset-react", {
"runtime": "automatic"
}]
]
}

And starting from Babel 8, "automatic" will be the default runtime.

You can also enable development mode for this new transform by using the new @babel/plugin-transform-react-jsx-development transform or by passing { "development": true, "runtime": "automatic" } to @babel/preset-react.

You can read mode about this new transform in the docs.