Skip to main content

7.23.0 Released: Decorator Metadata and many new `import` features!

· 6 min read

We just released Babel 7.23.0! 🎉

It includes transform support for the Decorator Metadata, Source Phase Import, Deferred Import Evaluation, and Optional Chaining Assignment proposals. We also updated our parser to support the new TypeScript 5.2 version, and added a transform option to let you use the .ts extension within TypeScript imports.

In addition to releasing version 7.23.0, we also recently published the first Babel 8 alpha release!

You can read the whole changelog on GitHub.

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!

Highlights

Decorator Metadata (#15895)

The decorator metadata Stage 3 proposal extends the Decorator proposal, allowing decorators to communicate with each other by sharing some metadata.

function write(_, context) {
context.metadata.message = "Hello from writer!";
}
function read(_, context) {
console.log(context.metadata.message);
}

@read // Logs "Hello from writer!"
class A {
@write x;
}

While we usually have one plugin per proposal, these two proposals are so closely related that we decided to include support for Decorator Metadata directly in @babel/plugin-proposal-decorators. If you are on the latest version of Babel and you are already using standard-track decorators, this proposal is enabled by default.

Source Phase Imports for WebAssembly modules (#15829, #15870)

The Source Phase Import Stage 3 proposal allows importing an object representation of the source of a module, without evaluating it and without loading its dependencies. You can load module sources using the import source syntax, for languages that have a defined source representation:

import source fooSource from "foo";

The motivating use case is to have static syntax to load WebAssembly.Module objects, rather than being forced to do so dynamically:

// Before
const url = import.meta.resolve("./my-wasm/module.wasm");
const responsePromise = fetch(url);
const mod = await WebAssembly.compileStreaming(responsePromise);

// After
import source mod from "./my-wasm/module.wasm";

Babel now supports transforming source imports for WebAssembly modules targeting Node.js, Web-compatible environments (browsers and Deno), or both, depending on your configured targets.

You can transform it using the @babel/plugin-proposal-import-wasm-source plugin:

{
"targets": ["chrome 90", "firefox 90", "node 20.6"],
"plugins": ["@babel/plugin-proposal-import-wasm-source"]
}

Deferred Import Evaluation (#15845, #15878)

The Deferred Import Evaluation Stage 2 proposal allows deferring the (synchronous) evaluation of imported modules until they are used. This can greatly improve the startup performance of your code when using modules that have a significant initialization cost.

You can use the new import defer syntax to defer your imports:

// this line does not evaluate ./mod
import defer * as myMod from "./mod";


later(() => {
// this one evaluates it!
use(myMod.foo);
})

The import defer syntax only support namespace imports, so the following is not valid:

import defer modDefault from "./mod";
import defer { named } from "./mod";

Babel only supports compiling import defer when compiling ECMAScript modules to CommonJS, using the @babel/plugin-proposal-import-defer plugin:

// babel.config.json
{
"plugins": [
"@babel/plugin-transform-modules-commonjs",
"@babel/plugin-proposal-import-defer"
]
}

If you are using Babel with a bundler and thus you are not compiling modules via Babel, if your bundler supports import defer you can use @babel/plugin-syntax-import-defer to allow parsing the new syntax.

Optional Chaining Assignment (#15751)

The Optional Chaining Assignment Stage 1 proposal allows using optional chaining on the left side of assignment operators:

maybeAnObj?.prop = theValue;

// Equivalent to

if (maybeAnObj != null) maybeAnObj.prop = theValue;

This proposal is championed by Nicolò Ribaudo, a member of the Babel team, and we are already implementing it at Stage 1 to gather feedback that can help the design process in TC39. If you have any comments, or any real-world examples or use cases you can share, please leave them in the proposal repository!

You can try this proposal using the @babel/plugin-proposal-optional-chaining-assign plugin. Given the early stage and thus the high possibility of breaking changes, you must specify which version you want to use (currently only 2023-07 is supported):

// babel.config.json
{
"plugins": [
["@babel/plugin-proposal-optional-chaining-assign", {
"version": "2023-07"
}]
]
}

TypeScript updates (#15896, #15913)

Babel 7.23.0 now supports parsing TypeScript 5.2, whose only syntactic addition is the introduction of tuple types having both labeled and unlabeled entries.

In addition to that, @babel/preset-typescript now has a rewriteImportExtensions option that, when enabled, will rewrite .ts/.mts/.cts extensions in your import declarations to .js/.mjs/.cjs. Together with TypeScript's allowImportingTsExtension option, this allows you to write complete relative specifiers in your imports while using the same extension used by the files you are authoring.

As an example, given this project structure (where src contains the source files, and dist the compiled files):

.
├── src
│ ├── main.ts
│ └── dep.ts
├── dist
│ ├── main.js
│ └── dep.js
├── babel.config.json
└── tsconfig.json

and with the following configuration files:

babel.config.jsontsconfig.json
{
"presets": [
["@babel/preset-typescript", {
"rewriteImportExtensions": true
}]
]
}
{
"compilerOptions": {
"lib": ["esnext"],
"noEmit": true,
"isolatedModules": true,
"moduleResolution": "nodenext",
"allowImportingTsExtensions": true
}
}

you will be able to write import x from "./dep.ts" in main.ts, and Babel will transform it to import x from "./dep.js" when compiling main.ts to main.js.

Babel 8 alpha

After multiple years working on it, we finally released Babel 8.0.0 alpha! 🥳

Future pre-release will include more breaking changes, so we do not recommend to use it in production unless you are willing to keep up with all the changes as they come. However, you can already start getting ready for what will be Babel 8.0.0 stable:

  • You can install it with npm install -D @babel/core@next (or the equivalent command with your package manager), and verify if it already works in your project. Please make sure that all the @babel/* packages in your package.json have exactly the same version (for example, 8.0.0-alpha.2).
  • You can read the migration guide for users: many of the breaking changes introduced in Babel 8 can already be enabled in Babel 7, and Babel 8 will simply remove the relative options making them the default behavior.
  • If you are a Babel plugin author, or if you use the Babel programmatic API directly, you can read the migration guide for integration authors and make sure that your library works well with Babel 8.

In addition to the migration guides, you can read the full changelog of the various Babel 8 pre-releases on GitHub.