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:
// 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:
const foo = ({ a = 1 }, b = 2, ...args) => [a, b, args];
It transforms it down to ES5-like parameters:
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:
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
}]
]
}
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:
{
"presets": [
["@babel/preset-typescript", {
"onlyRemoveTypeImports": true
}]
]
}
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:
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:
{
"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.
While it's not released in a stable release yet, you can try it out on the experimental React release channel:
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:
function Foo() {
return <div />;
}
would turn into:
import { jsx as _jsx } from "react/jsx-runtime";
function Foo() {
return _jsx("div", ...);
}
Note: The functions inside
react/jsx-runtime
andreact/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
):
{
"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.