A Babel plugin that compiles ES2015 for...of loops to ES5-compatible code. It provides comprehensive transformation support for arrays, iterables, and other objects with two modes: strict spec-compliant mode with full iterator protocol error handling, and loose mode optimized for performance.
npm install --save-dev babel-plugin-transform-es2015-for-ofThe plugin exports a single default function that creates a Babel plugin configuration:
// Default export from plugin module
export default function ({ messages, template, types: t }) {
// Returns Babel plugin object with visitor pattern
}Note: This is a Babel plugin, so it's typically not imported directly in application code. Instead, it's configured through Babel's configuration system.
{
"plugins": ["transform-es2015-for-of"]
}With options:
{
"plugins": [
["transform-es2015-for-of", {
"loose": true
}]
]
}babel --plugins transform-es2015-for-of script.jsrequire("babel-core").transform("code", {
plugins: ["transform-es2015-for-of"]
});The main export is a plugin factory function that creates a Babel plugin configuration.
/**
* Creates a Babel plugin for transforming ES2015 for...of loops
* @param {object} param0 - Destructured Babel core objects
* @param {object} param0.messages - Babel messages for error reporting
* @param {function} param0.template - Babel template helper for AST generation
* @param {object} param0.types - Babel types utility object (destructured as t)
* @returns {object} Babel plugin configuration with visitor pattern
*/
export default function ({ messages, template, types: t }) {
return {
visitor: {
ForOfStatement(path: NodePath<ForOfStatement>, state: PluginPass): void {
// Transform for...of statements based on configuration
}
}
};
}The plugin accepts configuration options through Babel's plugin system:
interface PluginOptions {
/** Enable loose mode for better performance with arrays (default: false) */
loose?: boolean;
}The plugin provides three transformation strategies:
For literal arrays, the plugin automatically generates optimized for loops:
Input:
for (let a of [1, 2, 3]) {
console.log(a);
}Output:
var _arr = [1, 2, 3];
for (var _i = 0; _i < _arr.length; _i++) {
var a = _arr[_i];
}Full iterator protocol compliance with proper error handling:
Input:
for (var i of foo) {
console.log(i);
}Output:
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = foo[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var i = _step.value;
console.log(i);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}Optimized for performance, especially with arrays:
Input:
for (var i of foo) {
console.log(i);
}Output:
for (var _iterator = foo, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
var _ref;
if (_isArray) {
if (_i >= _iterator.length) break;
_ref = _iterator[_i++];
} else {
_i = _iterator.next();
if (_i.done) break;
_ref = _i.value;
}
var i = _ref;
console.log(i);
}The plugin uses Babel's visitor pattern to transform AST nodes:
interface BabelPlugin {
visitor: {
/** Visitor method for ForOfStatement AST nodes */
ForOfStatement(path: NodePath, state: PluginPass): void;
};
}
interface NodePath<T = Node> {
/** The AST node being visited */
node: T;
/** Babel scope utilities */
scope: Scope;
/** Parent path in the AST */
parentPath: NodePath;
/** Replace this node with multiple nodes */
replaceWithMultiple(nodes: Node[]): void;
/** Ensure the node body is a block statement */
ensureBlock(): void;
/** Get child path by property name */
get(key: string): NodePath;
/** Remove this node from the AST */
remove(): void;
}
interface PluginPass {
/** Plugin options passed via configuration */
opts: PluginOptions;
}The plugin handles various for...of loop patterns:
// let declaration
for (let item of iterable) { }
// const declaration
for (const item of iterable) { }
// var declaration
for (var item of iterable) { }// Existing identifier
for (item of iterable) { }
// Destructuring pattern
for (const { name, age } of users) { }
// Array destructuring
for (const [key, value] of entries) { }
// Member expression
for (obj.prop of iterable) { }outer: for (const item of iterable) {
if (condition) break outer;
}The plugin requires:
babel-runtime: Runtime helpers for transformationThe plugin uses Babel's template system with three core templates for code generation:
/** Template for array literal optimization */
const buildForOfArray: (replacements: {
KEY: Node,
ARR: Node,
BODY: Node
}) => Node;
/** Template for loose mode transformation */
const buildForOfLoose: (replacements: {
LOOP_OBJECT: Node,
IS_ARRAY: Node,
OBJECT: Node,
INDEX: Node,
ID: Node
}) => Node;
/** Template for spec-compliant transformation */
const buildForOf: (replacements: {
ITERATOR_HAD_ERROR_KEY: Node,
ITERATOR_COMPLETION: Node,
ITERATOR_ERROR_KEY: Node,
ITERATOR_KEY: Node,
STEP_KEY: Node,
OBJECT: Node,
BODY: Node
}) => Node[];The plugin uses three internal functions to handle different transformation strategies:
/**
* Handles array literal optimization transformation
* @param {NodePath} path - The ForOfStatement path
* @returns {Node[]} Array of replacement nodes
*/
function _ForOfStatementArray(path: NodePath<ForOfStatement>): Node[];
/**
* Implements loose mode transformation for better performance
* @param {NodePath} path - The ForOfStatement path
* @param {object} file - Babel file object for error reporting
* @returns {TransformResult} Transformation result object
*/
function loose(path: NodePath<ForOfStatement>, file: BabelFile): TransformResult;
/**
* Implements spec-compliant transformation with full iterator protocol
* @param {NodePath} path - The ForOfStatement path
* @param {object} file - Babel file object for error reporting
* @returns {TransformResult} Transformation result object
*/
function spec(path: NodePath<ForOfStatement>, file: BabelFile): TransformResult;interface TransformResult {
/** Whether to replace the parent node instead of current node */
replaceParent: boolean;
/** Variable declaration node to inject into loop body */
declar?: Node;
/** The transformed loop node */
loop: Node;
/** Final replacement node(s) */
node: Node | Node[];
}In loose mode, an iterator's return method will not be called on abrupt completions caused by thrown errors. This is a performance trade-off for better array iteration speed.
The plugin throws errors for unsupported for...of left-hand side patterns using Babel's error reporting:
// This will cause a build error with code frame context
file.buildCodeFrameError(left, messages.get("unknownForHead", left.type));Error reporting uses Babel's message system with helpful code frame context showing the exact location of unsupported syntax.