Why Babel 7.18 broke async/await: regeneratorRuntime issues and fixes
This article analyzes the Babel 7.18 upgrade that caused regeneratorRuntime to disappear, leading to async/await failures and larger bundle sizes, explains the underlying plugin interactions, and provides practical solutions for frontend projects using Babel.
Background
Babel 7.18.0 was released as a supposedly compatible upgrade from 7.17.x, but it introduced several problems in the Ant Group ecosystem, such as missing regeneratorRuntime and increased bundle size.
Prerequisite Knowledge
Understanding the following is essential:
@babel/preset-env bundles many plugins and configurations.
@babel/plugin-transform-regenerator handles generator conversion for async/await.
@babel/plugin-transform-runtime provides various runtime helpers.
regeneratorRuntime is a global variable that many projects assumed existed.
regenerator-runtime is Facebook’s (Meta’s) open‑source library that defines regeneratorRuntime.
Syntax and API Polyfill
ECMAScript upgrades include syntax (e.g., arrow functions, async/await) and API (e.g., String.prototype.replaceAll). Babel plugins like @babel/plugin-transform-arrow-functions convert syntax, while core‑js and regenerator-runtime polyfill the APIs, all integrated by @babel/preset-env.
async/await conversion
Conversion occurs in two steps: async → generator, then generator → ES5. The second step relies on @babel/plugin-transform-regenerator, which expects a global regeneratorRuntime variable.
There are three ways to provide this variable:
Manually import the runtime: import 'regenerator-runtime/runtime' Configure @babel/preset-env with useBuiltIns: 'usage' so Babel adds the import automatically.
Use @babel/plugin-transform-runtime (explained below).
@babel/plugin-transform-runtime
This plugin has three main functions:
Enhances async/await conversion so code works even without a global regeneratorRuntime (enabled by default).
Provides non‑global polyfills for new APIs (disabled by default).
Extracts inline helper code (e.g., objectSpread, classCallCheck) into imports like import xxx from '@babel/runtime/helpers/xxx', reducing duplicate code and bundle size (enabled by default).
Typical use cases:
Component library builds benefit from all three functions.
Application builds mainly use function 1; function 2 is less useful; function 3 helps shrink bundles.
Frameworks such as Umi enable this plugin by default, while tools like father require manual activation.
regenerator-runtime
regenerator-runtime is an open‑source library that attaches regeneratorRuntime to the global object. Although the plugin claims not to pollute the global scope, the library itself still creates the global variable.
What Babel changed
The new release addressed three issues:
Async/await output no longer depends on a global regeneratorRuntime; the helper is inlined.
regeneratorRuntime becomes a Babel runtime helper that can be extracted via @babel/plugin-transform-runtime.
The code of regenerator-runtime is copied into the Babel repository, removing the external dependency.
These changes caused incompatibilities because some frameworks still expected the global variable.
Problem Analysis
1. regeneratorRuntime is undefined
Older projects that relied on the global variable started failing after upgrading to Babel 7.18.0. The new inline helper does not automatically attach regeneratorRuntime to the global scope, breaking code that still expects it.
Example scenario with Ant’s Smallfish mobile framework:
Smallfish does not lock the version of @babel/plugin-transform-regenerator, so the inline helper is used.
Mobile builds often disable polyfills to save size, so the external regenerator-runtime is not loaded.
When a library built with an older Babel version (but re‑compiled with 7.18.0) is included, it may still require a global regeneratorRuntime, leading to runtime errors.
2. Bundle size increase
When @babel/plugin-transform-regenerator inlines the helper but @babel/plugin-transform-runtime is locked to an older version, the inline 10 KB helper cannot be extracted, causing each async/await usage to embed the helper and inflate the bundle.
Typical fix: align the versions of both plugins or lock @babel/plugin-transform-regenerator to a lower version (e.g., ~7.12.0) via resolutions in package.json.
3. npm package issues
Enabling @babel/plugin-transform-runtime is recommended to both eliminate the global regeneratorRuntime dependency and extract helpers to reduce size. For tools like father, ensure the runtimeHelpers option is enabled and update @babel/runtime to the matching version.
Solutions and Conclusions
The author quickly reverted the missing global assignment by adding the try‑catch block back in a new Babel PR (https://github.com/babel/babel/pull/14581). The current state is:
Babel 7.18.x is functionally equivalent to 7.17.x for existing projects.
The regeneratorRuntime undefined issue has been resolved.
Bundle size problems can be mitigated by aligning plugin versions or using the runtime plugin to extract helpers.
Overall, the frontend build ecosystem is complex and carries historical baggage; choosing well‑maintained internal frameworks and keeping Babel dependencies consistent helps avoid these pitfalls.
Other
Stay tuned to the Alibaba F2E WeChat public account for the latest frontend developments.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
