A Babel plugin that transforms async generator functions and for-await-of loops into ES2015 generators and manual iterator handling, enabling async generators to run in environments that support generators but not async generators.
npm install --save-dev @babel/plugin-transform-async-generator-functions// babel.config.js or .babelrc.js
module.exports = {
plugins: ["@babel/plugin-transform-async-generator-functions"]
};For programmatic usage:
import { transform } from "@babel/core";
import asyncGeneratorPlugin from "@babel/plugin-transform-async-generator-functions";
const result = transform(code, {
plugins: [asyncGeneratorPlugin]
});This plugin automatically transforms async generator functions and for-await-of loops. No configuration is required.
Input:
async function* asyncGenerator() {
await 1;
yield 2;
yield* otherAsyncGenerator();
}
async function example() {
for await (const item of asyncIterable) {
console.log(item);
}
}Output:
function asyncGenerator() {
return _asyncGenerator.apply(this, arguments);
}
function _asyncGenerator() {
_asyncGenerator = babelHelpers.wrapAsyncGenerator(function* () {
yield babelHelpers.awaitAsyncGenerator(1);
yield 2;
yield* babelHelpers.asyncGeneratorDelegate(
babelHelpers.asyncIterator(otherAsyncGenerator())
);
});
return _asyncGenerator.apply(this, arguments);
}
function example() {
return _example.apply(this, arguments);
}
function _example() {
_example = babelHelpers.wrapAsyncGenerator(function* () {
var _iteratorAbruptCompletion = false;
var _didIteratorError = false;
var _iteratorError;
try {
for (var _iterator = babelHelpers.asyncIterator(asyncIterable), _step;
_iteratorAbruptCompletion = !(_step = yield babelHelpers.awaitAsyncGenerator(_iterator.next())).done;
_iteratorAbruptCompletion = false) {
const item = _step.value;
console.log(item);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (_iteratorAbruptCompletion && _iterator.return != null) {
yield babelHelpers.awaitAsyncGenerator(_iterator.return());
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
});
return _example.apply(this, arguments);
}The plugin operates through a two-phase transformation process:
for await (... of ...) loops into try/catch/finally blocks with manual iterator handlingasync function* name() declarations into regular generators wrapped with helper functionsThe plugin uses Babel's helper system to inject runtime utilities that handle async iteration protocols and generator wrapping.
The main plugin export that integrates with Babel's transformation pipeline.
/**
* Main Babel plugin function created using declare() from @babel/helper-plugin-utils
* @param api - Babel API object with version assertion and utilities
* @returns Babel plugin object with visitor and metadata
*/
declare function createPlugin(api: BabelAPI): BabelPlugin;
export default createPlugin;
interface BabelPlugin {
name: string;
visitor: {
Program(path: NodePath<t.Program>, state: PluginPass): void;
};
manipulateOptions?: (opts: any, parserOpts: { plugins: string[] }) => void;
}Transforms async generator functions into regular generators wrapped with async helpers.
Transformation Scope:
async function* name() { ... } declarationsconst fn = async function* () { ... } expressions{ async *method() { ... } } object methodsclass { async *method() { ... } } class methodsKey Transformations:
await expr → yield babelHelpers.awaitAsyncGenerator(expr)yield* expr → yield* babelHelpers.asyncGeneratorDelegate(babelHelpers.asyncIterator(expr), babelHelpers.awaitAsyncGenerator) (Babel 7)yield* expr → yield* babelHelpers.asyncGeneratorDelegate(babelHelpers.asyncIterator(expr)) (Babel 8)babelHelpers.wrapAsyncGenerator(function* () { ... })path.setData("@babel/plugin-transform-async-generator-functions/async_generator_function", true)Transforms for-await-of loops into equivalent try/catch/finally iterator handling code.
Transformation Scope:
for await (item of iterable) { ... } loops within async functions and async generatorsconst item, let { prop }, var [index]label: for await (...) { ... }Generated Structure:
babelHelpers.asyncIterator(iterable).next() calls.return() methodThe plugin automatically injects these Babel runtime helpers when needed:
// Async iterator creation
declare function asyncIterator(iterable: any): AsyncIterator<any>;
// Async generator delegation for yield* (Babel 7 includes awaitWrap, Babel 8 does not)
declare function asyncGeneratorDelegate(
iterable: AsyncIterable<any>,
awaitWrap?: Function
): AsyncGenerator<any>;
// Wraps await expressions in async generators
declare function awaitAsyncGenerator(value: any): any;
// Wraps entire async generator functions
declare function wrapAsyncGenerator(fn: GeneratorFunction): Function;Automatically enables the "asyncGenerators" parser plugin in Babel 7 to parse async generator syntax. In Babel 8, this is handled automatically and the manipulateOptions function is undefined.
interface ManipulateOptions {
(opts: any, parserOpts: { plugins: string[] }): void;
}
// The actual manipulateOptions implementation (Babel 7 only)
manipulateOptions: process.env.BABEL_8_BREAKING
? undefined
: (_, parser) => parser.plugins.push("asyncGenerators")Uses Babel's visitor pattern to traverse and transform AST nodes.
interface PluginVisitor {
Program(path: NodePath<t.Program>, state: PluginPass): void;
}
// Internal visitor used by the plugin
interface InternalVisitor {
Function(path: NodePath<t.Function>, state: PluginPass): void;
}
// Environment visitors for specific transformations
interface ForAwaitVisitor {
ArrowFunctionExpression(path: NodePath<t.ArrowFunctionExpression>): void;
ForOfStatement(path: NodePath<t.ForOfStatement>, state: { file: BabelFile }): void;
}
interface YieldStarVisitor {
ArrowFunctionExpression(path: NodePath<t.ArrowFunctionExpression>): void;
YieldExpression(path: NodePath<t.YieldExpression>, state: PluginPass): void;
}// Babel API types
interface BabelAPI {
assertVersion(version: number | string): void;
}
// Babel File object
interface BabelFile {
addHelper(name: string): t.Identifier;
}
// Babel types namespace
declare namespace t {
interface Program extends Node {}
interface Function extends Node {}
interface ForOfStatement extends Node {
await?: boolean;
left: VariableDeclaration | LVal;
right: Expression;
body: Statement;
}
interface YieldExpression extends Node {
delegate?: boolean;
argument?: Expression;
}
interface ArrowFunctionExpression extends Function {}
interface Identifier extends LVal {
name: string;
}
interface CallExpression extends Expression {
callee: Expression;
arguments: Array<Expression | SpreadElement>;
}
}
interface PluginPass {
addHelper(name: string): t.Identifier;
file: BabelFile;
}
interface NodePath<T> {
node: T;
scope: Scope;
parent: Node;
parentPath: NodePath<Node>;
traverse(visitor: Visitor, state?: any): void;
skip(): void;
replaceWithMultiple(nodes: Node[]): void;
ensureBlock(): void;
setData(key: string, value: any): void;
}
// Base types referenced above
interface Node {
type: string;
}
interface Expression extends Node {}
interface Statement extends Node {}
interface LVal extends Node {}
interface VariableDeclaration extends Statement {}
interface SpreadElement extends Node {}
interface Scope {
generateUidIdentifier(name: string): t.Identifier;
parent: Scope;
crawl(): void;
}
interface Visitor<T = any> {
[key: string]: (path: NodePath<Node>, state?: T) => void;
}
// Transform result types
interface RewriteResult {
replaceParent: boolean;
node: t.Statement[];
declar: t.Statement | undefined;
loop: t.ForStatement;
}The plugin handles various edge cases:
.return() method in finally blocksThis plugin accepts no configuration options. It transforms all async generator functions and for-await-of loops encountered in the code.
Usage in babel.config.js:
module.exports = {
plugins: [
"@babel/plugin-transform-async-generator-functions"
]
};Usage with other plugins:
module.exports = {
plugins: [
"@babel/plugin-transform-async-generator-functions",
"@babel/plugin-transform-async-to-generator"
]
};