UNPKG

@babel/plugin-transform-for-of

Version:
181 lines (178 loc) 7.04 kB
import { declare } from '@babel/helper-plugin-utils'; import { types, template } from '@babel/core'; import { skipTransparentExprWrapperNodes } from '@babel/helper-skip-transparent-expression-wrappers'; function buildLoopBody(path, declar, newBody) { let block; const bodyPath = path.get("body"); const body = newBody ?? bodyPath.node; if (types.isBlockStatement(body) && Object.keys(path.getBindingIdentifiers()).some(id => bodyPath.scope.hasOwnBinding(id))) { block = types.blockStatement([declar, body]); } else { block = types.toBlock(body); block.body.unshift(declar); } return block; } const index = declare((api, options) => { api.assertVersion("^7.0.0-0 || ^8.0.0"); if ("loose" in options) { console.warn("@babel/plugin-transform-for-of: The 'loose' option has been deprecated, " + "use the 'iterableIsArray' and 'skipForOfIteratorClosing' assumptions instead (https://babeljs.io/assumptions)."); } { const { assumeArray, allowArrayLike, loose } = options; if (loose === true && assumeArray === true) { throw new Error(`The loose and assumeArray options cannot be used together in @babel/plugin-transform-for-of`); } if (assumeArray === true && allowArrayLike === true) { throw new Error(`The assumeArray and allowArrayLike options cannot be used together in @babel/plugin-transform-for-of`); } } const iterableIsArray = options.assumeArray ?? (!options.loose && api.assumption("iterableIsArray")); const arrayLikeIsIterable = options.allowArrayLike ?? api.assumption("arrayLikeIsIterable"); const skipIteratorClosing = api.assumption("skipForOfIteratorClosing") ?? options.loose; if (iterableIsArray && arrayLikeIsIterable) { throw new Error(`The "iterableIsArray" and "arrayLikeIsIterable" assumptions are not compatible.`); } if (iterableIsArray) { return { name: "transform-for-of", visitor: { ForOfStatement(path) { const { scope } = path; const { left, await: isAwait } = path.node; if (isAwait) { return; } const right = skipTransparentExprWrapperNodes(path.node.right); const i = scope.generateUidIdentifier("i"); let array = scope.maybeGenerateMemoised(right, true); if (!array && types.isIdentifier(right) && path.get("body").scope.hasOwnBinding(right.name)) { array = scope.generateUidIdentifier("arr"); } const inits = [types.variableDeclarator(i, types.numericLiteral(0))]; if (array) { inits.push(types.variableDeclarator(array, right)); } else { array = right; } const item = types.memberExpression(types.cloneNode(array), types.cloneNode(i), true); let assignment; if (types.isVariableDeclaration(left)) { assignment = left; assignment.declarations[0].init = item; } else { assignment = types.expressionStatement(types.assignmentExpression("=", left, item)); } path.replaceWith(types.forStatement(types.variableDeclaration("let", inits), types.binaryExpression("<", types.cloneNode(i), types.memberExpression(types.cloneNode(array), types.identifier("length"))), types.updateExpression("++", types.cloneNode(i)), buildLoopBody(path, assignment))); } } }; } const buildForOfArray = template` for (var KEY = 0, NAME = ARR; KEY < NAME.length; KEY++) BODY; `; const buildForOfNoIteratorClosing = template.statements` for (var ITERATOR_HELPER = CREATE_ITERATOR_HELPER(OBJECT, ARRAY_LIKE_IS_ITERABLE), STEP_KEY; !(STEP_KEY = ITERATOR_HELPER()).done;) BODY; `; const buildForOf = template.statements` var ITERATOR_HELPER = CREATE_ITERATOR_HELPER(OBJECT, ARRAY_LIKE_IS_ITERABLE), STEP_KEY; try { for (ITERATOR_HELPER.s(); !(STEP_KEY = ITERATOR_HELPER.n()).done;) BODY; } catch (err) { ITERATOR_HELPER.e(err); } finally { ITERATOR_HELPER.f(); } `; const builder = skipIteratorClosing ? { build: buildForOfNoIteratorClosing, helper: "createForOfIteratorHelperLoose", getContainer: nodes => nodes } : { build: buildForOf, helper: "createForOfIteratorHelper", getContainer: nodes => nodes[1].block.body }; function _ForOfStatementArray(path) { const { node, scope } = path; const right = scope.generateUidIdentifierBasedOnNode(node.right, "arr"); const iterationKey = scope.generateUidIdentifier("i"); const loop = buildForOfArray({ BODY: node.body, KEY: iterationKey, NAME: right, ARR: node.right }); types.inherits(loop, node); const iterationValue = types.memberExpression(types.cloneNode(right), types.cloneNode(iterationKey), true); let declar; const left = node.left; if (types.isVariableDeclaration(left)) { left.declarations[0].init = iterationValue; declar = left; } else { declar = types.expressionStatement(types.assignmentExpression("=", left, iterationValue)); } loop.body = buildLoopBody(path, declar, loop.body); return loop; } return { name: "transform-for-of", visitor: { ForOfStatement(path, state) { const right = path.get("right"); if (right.isArrayExpression() || right.isGenericType("Array")) { path.replaceWith(_ForOfStatementArray(path)); return; } const { node, parent, scope } = path; const left = node.left; let declar; const stepKey = scope.generateUid("step"); const stepValue = types.memberExpression(types.identifier(stepKey), types.identifier("value")); if (types.isVariableDeclaration(left)) { declar = types.variableDeclaration(left.kind, [types.variableDeclarator(left.declarations[0].id, stepValue)]); } else { declar = types.expressionStatement(types.assignmentExpression("=", left, stepValue)); } const nodes = builder.build({ CREATE_ITERATOR_HELPER: state.addHelper(builder.helper), ITERATOR_HELPER: scope.generateUidIdentifier("iterator"), ARRAY_LIKE_IS_ITERABLE: arrayLikeIsIterable ? types.booleanLiteral(true) : null, STEP_KEY: types.identifier(stepKey), OBJECT: node.right, BODY: buildLoopBody(path, declar) }); const container = builder.getContainer(nodes); types.inherits(container[0], node); types.inherits(container[0].body, node.body); if (types.isLabeledStatement(parent)) { container[0] = types.labeledStatement(parent.label, container[0]); path.parentPath.replaceWithMultiple(nodes); path.skip(); } else { path.replaceWithMultiple(nodes); } } } }; }); export { index as default }; //# sourceMappingURL=index.js.map