UNPKG

babel-plugin-transform-async-to-promises

Version:
1,083 lines 187 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const helpers_string_1 = require("./helpers-string"); const defaultConfigValues = { externalHelpers: false, hoist: false, inlineHelpers: false, minify: false, target: "es5", topLevelAwait: "disabled", }; function readConfigKey(config, key) { if (Object.hasOwnProperty.call(config, key)) { const result = config[key]; if (typeof result !== "undefined") { return result; } } return defaultConfigValues[key]; } function discardingIntrinsics(node) { if (node.type == "V8IntrinsicIdentifier") { throw new Error(`Expected either an expression or a statement, got a ${node.type}!`); } return node; } function clearDeclarationData(declaration) { let path = declaration; while (path) { if (path.getData("declaration:var:2") == declaration) { path.setData("declaration:var:2", null); } path = path.parentPath; } } const constantFunctionMethods = { "call": false, "apply": false, "bind": false, }; const constantStaticMethods = { "Object": Object.assign({ "assign": true, "create": true, "defineProperty": true, "defineProperties": true, "entries": true, "freeze": true, "fromEntries": true, "getOwnPropertyDescriptor": true, "getOwnPropertyDescriptors": true, "getOwnPropertyNames": true, "getOwnPropertySymbols": true, "getPrototypeOf": true, "is": true, "isExtensible": true, "isFrozen": true, "isSealed": true, "keys": true, "preventExtensions": true, "seal": true, "setPrototypeOf": true, "values": true }, constantFunctionMethods), "Function": constantFunctionMethods, "Boolean": constantFunctionMethods, "Number": Object.assign({ "isNaN": true, "isFinite": true, "isInteger": true, "isSafeInteger": true, "parseFloat": true, "parseInteger": true }, constantFunctionMethods), "Array": Object.assign({ "from": true, "isArray": true, "of": true }, constantFunctionMethods), "Date": Object.assign({ "now": true, "parse": true, "UTC": true }, constantFunctionMethods), "RegExp": constantFunctionMethods, "Error": constantFunctionMethods, "TypeError": constantFunctionMethods, "Map": constantFunctionMethods, "Set": constantFunctionMethods, "WeakMap": constantFunctionMethods, "WeakSet": constantFunctionMethods, "Promise": Object.assign({ "all": true, "race": true, "resolve": true, "reject": true }, constantFunctionMethods), "Math": { "abs": true, "acos": true, "asin": true, "atan": true, "atan2": true, "ceil": true, "cos": true, "exp": true, "floor": true, "log": true, "max": true, "min": true, "pow": true, "random": true, "round": true, "sin": true, "sqrt": true, "tan": true, }, "JSON": { "parse": true, "stringify": true, }, "URL": Object.assign({ "createObjectURL": true, "revokeObjectURL": true }, constantFunctionMethods), "console": { "assert": true, "clear": true, "count": true, "error": true, "info": true, "log": true, "warn": true, }, "document": { "createComment": true, "createElement": true, "createTextNode": true, "getElementsByClassName": true, "getElementsByTagName": true, "getElementsByName": true, "getElementById": true, "querySelector": true, "querySelectorAll": true, "write": true, "writeln": true, }, "XMLHttpRequest": constantFunctionMethods, "WebSocket": constantFunctionMethods, "Image": constantFunctionMethods, "alert": constantFunctionMethods, "confirm": constantFunctionMethods, "open": constantFunctionMethods, "prompt": constantFunctionMethods, "eval": constantFunctionMethods, "isFinite": constantFunctionMethods, "isNaN": constantFunctionMethods, "parseInt": constantFunctionMethods, "parseFloat": constantFunctionMethods, "decodeURI": constantFunctionMethods, "decodeURIComponent": constantFunctionMethods, "encodeURI": constantFunctionMethods, "encodeURIComponent": constantFunctionMethods, "escape": constantFunctionMethods, "unescape": constantFunctionMethods, "$": constantFunctionMethods, }; const originalNodeMap = new WeakMap(); const skipNodeSet = new WeakSet(); const breakIdentifierMap = new WeakMap(); const isHelperDefinitionSet = new WeakSet(); const helperNameMap = new WeakMap(); const nodeIsAsyncSet = new WeakSet(); let helpers; const alwaysTruthy = Object.keys(constantStaticMethods); const numberNames = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]; function default_1({ types, traverse, transformFromAst, version, }) { const isNewBabel = !/^6\./.test(version); function cloneNode(node) { const result = types.cloneDeep(node); if (types.isIdentifier(node) || types.isMemberExpression(node)) { const helperName = helperNameMap.get(node); if (helperName !== undefined) { helperNameMap.set(result, helperName); } } return result; } function wrapNodeInStatement(node) { if (types.isStatement(node)) { return types.blockStatement([node]); } if (types.isExpression(node)) { return types.expressionStatement(node); } throw new Error(`Expected either an expression or a statement, got a ${node.type}!`); } function pathForNewNode(node, parentPath) { let contextPath = parentPath; while (contextPath != null) { if (contextPath.context) { const result = contextPath.context.create(parentPath.node, [node], 0, "dummy"); result.setContext(contextPath.context); return result; } contextPath = contextPath.parentPath; } throw parentPath.buildCodeFrameError(`Unable to find a context upon which to traverse!`, TypeError); } function pathsPassTest(matchingNodeTest, referenceOriginalNodes) { function visit(path, result, state) { if (referenceOriginalNodes) { const originalNode = originalNodeMap.get(path.node); if (originalNode) { traverse(wrapNodeInStatement(originalNode), visitor, path.scope, { match: result, state }, path); return false; } } const doesMatch = matchingNodeTest(path); if (doesMatch) { result.any = true; result.all = !(state.breakingLabels.length || state.unnamedBreak); } if (path.isBreakStatement()) { const label = path.node.label; if (!label) { state.unnamedBreak = true; } else if (state.breakingLabels.indexOf(label.name) === -1) { state.breakingLabels.push(label.name); } } if (path.isLabeledStatement()) { const index = state.breakingLabels.indexOf(path.node.label.name); if (index !== -1) { state.breakingLabels.splice(index, 1); } } if (path.isLoop()) { state.unnamedBreak = false; } if (doesMatch) { return false; } if (path.isConditional()) { const test = match(path.get("test"), state); const consequent = match(path.get("consequent"), state); const alternate = match(path.get("alternate"), state); result.any = result.any || test.any || consequent.any || alternate.any; return (result.all = (test.all || (consequent.all && alternate.all)) && !(state.breakingLabels.length || state.unnamedBreak)); } if (path.isSwitchStatement()) { const discriminant = match(path.get("discriminant"), state); const cases = path.get("cases"); const caseMatches = cases.map((switchCase, i) => { const newState = { unnamedBreak: false, breakingLabels: state.breakingLabels }; const newResult = match(switchCase, newState); for (i++; (!newResult.all || pathsBreakReturnOrThrow(switchCase).all) && i < cases.length; i++) { const tailMatch = match(cases[i], newState); newResult.all = (newResult.all || tailMatch.all) && !(state.breakingLabels.length || state.unnamedBreak); newResult.any = newResult.any || tailMatch.any; } return newResult; }); result.any = result.any || discriminant.any || caseMatches.some((caseMatch) => caseMatch.any); return (result.all = (discriminant.all || (cases.some((switchCase) => !switchCase.node.test) && caseMatches.every((caseMatch) => caseMatch.all))) && !(state.breakingLabels.length || state.unnamedBreak)); } if (path.isDoWhileStatement()) { const body = match(path.get("body"), { unnamedBreak: false, breakingLabels: state.breakingLabels }); const test = match(path.get("test"), state); result.any = result.any || body.any || test.any; return (result.all = (body.all || test.all) && !(state.breakingLabels.length || state.unnamedBreak)); } if (path.isWhileStatement()) { const testPath = path.get("test"); const test = match(testPath, state); const body = match(path.get("body"), { unnamedBreak: false, breakingLabels: state.breakingLabels }); result.any = result.any || test.any || body.any; return (result.all = (test.all || (body.all && extractLooseBooleanValue(testPath.node) === true)) && !(state.breakingLabels.length || state.unnamedBreak)); } if (path.isForXStatement()) { const right = match(path.get("right"), state); const body = match(path.get("body"), { unnamedBreak: false, breakingLabels: state.breakingLabels }); result.any = result.any || right.any || body.any; return (result.all = right.all && !(state.breakingLabels.length || state.unnamedBreak)); } if (path.isForStatement()) { const init = match(path.get("init"), state); const test = match(path.get("test"), state); const body = match(path.get("body"), { unnamedBreak: false, breakingLabels: state.breakingLabels }); const update = match(path.get("update"), state); result.any = result.any || init.any || test.any || body.any || update.any; return (result.all = (init.all || test.all) && !(state.breakingLabels.length || state.unnamedBreak)); } if (path.isLogicalExpression()) { const left = match(path.get("left"), state); const right = match(path.get("right"), state); result.any = result.any || left.any || right.any; return (result.all = left.all && !(state.breakingLabels.length || state.unnamedBreak)); } if (path.isReturnStatement()) { return true; } if (path.isBreakStatement()) { return true; } if (path.isContinueStatement()) { return true; } if (path.isThrowStatement()) { return true; } if (path.isTryStatement()) { const blockMatch = match(path.get("block"), state); const finalizer = path.get("finalizer"); const finalizerMatch = match(finalizer, state); const handler = path.get("handler"); const handlerMatch = match(handler, state); result.any = result.any || blockMatch.any || handlerMatch.any || finalizerMatch.any; if (finalizerMatch.all) { return (result.all = !(state.breakingLabels.length || state.unnamedBreak)); } else if (!finalizer.node) { return (result.all = handlerMatch.all && blockMatch.all && !(state.breakingLabels.length || state.unnamedBreak)); } return false; } if (path.isFunction()) { return false; } } const visitor = { enter(path) { switch (visit(path, this.match, this.state)) { case true: path.stop(); break; case false: path.skip(); break; } }, }; function match(path, state) { const match = { all: false, any: false }; if (path && path.node) { if (typeof visit(path, match, state) === "undefined") { path.traverse(visitor, { match, state }); } } return match; } return (path) => match(path, { breakingLabels: [], unnamedBreak: false }); } function pathsReachNodeTypes(matchingNodeTypes, referenceOriginalNodes) { return pathsPassTest((path) => path.type !== null && path.type !== undefined && matchingNodeTypes.indexOf(path.type) !== -1, referenceOriginalNodes); } const pathsReturn = pathsReachNodeTypes(["ReturnStatement"], true); const pathsReturnOrThrow = pathsReachNodeTypes(["ReturnStatement", "ThrowStatement"], true); const pathsReturnOrThrowCurrentNodes = pathsReachNodeTypes(["ReturnStatement", "ThrowStatement"], false); const pathsBreak = pathsReachNodeTypes(["BreakStatement"], true); const pathsBreakReturnOrThrow = pathsReachNodeTypes(["ReturnStatement", "ThrowStatement", "BreakStatement"], true); function isNonEmptyStatement(statement) { return !types.isEmptyStatement(statement); } function expressionInSingleReturnStatement(target) { const body = target.body; if (types.isBlockStatement(body)) { const statements = body.body.filter(isNonEmptyStatement); if (statements.length === 0) { return voidExpression(); } else { const firstStatement = statements[0]; if (types.isReturnStatement(firstStatement)) { return firstStatement.argument || voidExpression(); } } } else { return body; } } function propertyNameOfMemberExpression(node) { const property = node.property; if (node.computed) { if (types.isStringLiteral(property)) { return property.value; } } else { if (types.isIdentifier(property)) { return property.name; } } } function identifiersInForToLengthStatement(statement) { const init = statement.get("init"); if (init.isVariableDeclaration() && init.node.declarations.length === 1) { const declaration = init.get("declarations")[0]; if (types.isNumericLiteral(declaration.node.init) && declaration.node.init.value === 0) { const i = declaration.node.id; const test = statement.get("test"); if (types.isIdentifier(i) && test.isBinaryExpression() && test.node.operator === "<" && types.isIdentifier(test.node.left) && test.node.left.name === i.name) { const right = test.get("right"); if (right.isMemberExpression()) { const object = right.node.object; if (types.isIdentifier(object) && propertyNameOfMemberExpression(right.node) === "length") { const update = statement.get("update"); if (update.isUpdateExpression() && update.node.operator == "++" && types.isIdentifier(update.node.argument) && update.node.argument.name === i.name) { const binding = statement.scope.getBinding(i.name); if (binding) { const updateArgument = update.get("argument"); if (!binding.constantViolations.some((cv) => cv !== updateArgument && cv !== update)) { return { i, array: object, }; } } } } } } } } } function extractForOwnBodyPath(path) { let left = path.get("left"); if (left.isVariableDeclaration()) { left = left.get("declarations")[0].get("id"); } const right = path.get("right"); if (left.isIdentifier() && right.isIdentifier()) { const rightBinding = path.scope.getBinding(right.node.name); if (rightBinding && rightBinding.constant) { let body = path.get("body"); for (;;) { let statements; if (body.isBlockStatement()) { statements = body.get("body"); } else if (body.isReturnStatement()) { const argument = body.get("argument"); if (argument.isCallExpression() && invokeTypeOfExpression(argument) && argument.get("arguments").length === 1) { const firstArgument = argument.get("arguments")[0]; if (firstArgument.isFunctionExpression()) { statements = firstArgument.get("body").get("body"); } else { break; } } else { break; } } else { break; } if (statements.length !== 1) { return; } body = statements[0]; } if (body.isIfStatement() && !body.node.alternate) { const test = body.get("test"); if (test.isCallExpression() && test.node.arguments.length === 2) { const args = test.get("arguments"); const firstArg = args[0]; const secondArg = args[1]; if (firstArg.isIdentifier() && firstArg.node.name === right.node.name && secondArg.isIdentifier() && secondArg.node.name === left.node.name) { const callee = test.get("callee"); if (callee.isMemberExpression() && propertyNameOfMemberExpression(callee.node) === "call") { let method = callee.get("object"); if (method.isMemberExpression() && propertyNameOfMemberExpression(method.node) === "hasOwnProperty") { let target = method.get("object"); if (target.isObjectExpression() && target.node.properties.length === 0) { return body.get("consequent"); } if (target.isMemberExpression() && propertyNameOfMemberExpression(target.node) === "prototype") { target = target.get("object"); } if (target.isIdentifier() && target.node.name === "Object") { return body.get("consequent"); } } } } } } } } } function isContinuation(possible) { return ((types.isFunctionExpression(possible) && possible.id === null) || types.isArrowFunctionExpression(possible)); } function isPassthroughContinuation(continuation) { if (continuation) { if (isContinuation(continuation) && continuation.params.length === 1) { const expression = expressionInSingleReturnStatement(continuation); if (expression) { const firstParam = continuation.params[0]; if (types.isIdentifier(firstParam)) { const valueName = firstParam.name; if (types.isIdentifier(expression) && expression.name === valueName) { return true; } if (types.isConditionalExpression(expression) && types.isIdentifier(expression.test) && types.isIdentifier(expression.consequent) && expression.consequent.name === valueName && types.isIdentifier(expression.alternate) && expression.alternate.name === valueName) { return true; } } } } } return false; } function isEmptyContinuation(continuation) { if (types.isIdentifier(continuation)) { return helperNameMap.get(continuation) === "_empty"; } if (isContinuation(continuation)) { const body = continuation.body; if (types.isBlockStatement(body)) { return body.body.length === 0; } } return false; } function voidExpression(arg) { return types.unaryExpression("void", arg || types.numericLiteral(0)); } function simplifyWithIdentifier(expression, identifier, truthy) { if (types.isCallExpression(expression)) { switch (promiseCallExpressionType(expression)) { case "all": case "race": case "reject": case "resolve": { const firstArgument = expression.arguments[0]; if (types.isExpression(firstArgument)) { const simplified = simplifyWithIdentifier(firstArgument, identifier, truthy); return simplified === expression.arguments[0] ? expression : types.callExpression(expression.callee, [simplified]); } } case "then": { const callee = expression.callee; if (types.isMemberExpression(callee)) { const thenArgument = expression.arguments[0]; const object = callee.object; if (types.isCallExpression(object)) { const valueArgument = object.arguments[0]; if (types.isExpression(valueArgument) && types.isExpression(thenArgument)) { const simplified = simplifyWithIdentifier(valueArgument, identifier, truthy); return simplified === valueArgument ? expression : callThenMethod(types.callExpression(object.callee, [simplified]), thenArgument); } } } } } if ((expression.arguments.length === 1 && types.isIdentifier(expression.callee)) || isContinuation(expression.callee)) { const firstArgument = expression.arguments[0]; if (types.isExpression(firstArgument)) { const simplified = simplifyWithIdentifier(firstArgument, identifier, truthy); return simplified === expression.arguments[0] ? expression : types.callExpression(expression.callee, [simplified]); } } } if (types.isConditionalExpression(expression) && types.isIdentifier(expression.test) && expression.test.name === identifier.name) { return truthy ? expression.consequent : expression.alternate; } if (types.isLogicalExpression(expression) && types.isIdentifier(expression.left) && expression.left.name === identifier.name) { if (expression.operator === "&&") { return truthy ? expression.right : expression.left; } if (expression.operator === "||") { return truthy ? expression.left : expression.right; } } return expression; } function isIdentifierOrLiteral(expression) { return types.isIdentifier(expression) || types.isLiteral(expression); } function simpleExpressionForContinuation(continuation, value) { if (isContinuation(continuation)) { let expression = expressionInSingleReturnStatement(continuation); if (expression) { switch (continuation.params.length) { case 0: if ((types.isConditionalExpression(expression) && isIdentifierOrLiteral(expression.test) && isIdentifierOrLiteral(expression.consequent) && isIdentifierOrLiteral(expression.alternate)) || ((types.isLogicalExpression(expression) || types.isBinaryExpression(expression)) && isIdentifierOrLiteral(expression.left) && isIdentifierOrLiteral(expression.right)) || (types.isUnaryExpression(expression) && isIdentifierOrLiteral(expression.argument)) || (types.isCallExpression(expression) && isIdentifierOrLiteral(expression.callee) && expression.arguments.length === 0) || isIdentifierOrLiteral(expression)) { return expression; } break; case 1: { if (!value) { return; } const firstParam = continuation.params[0]; const replace = (expr) => types.isIdentifier(firstParam) && types.isIdentifier(expr) && expr.name === firstParam.name ? value : discardingIntrinsics(expr); if (isIdentifierOrLiteral(expression)) { return replace(expression); } if (types.isConditionalExpression(expression) && isIdentifierOrLiteral(expression.test) && isIdentifierOrLiteral(expression.consequent) && isIdentifierOrLiteral(expression.alternate)) { return types.conditionalExpression(replace(expression.test), replace(expression.consequent), replace(expression.alternate)); } if (types.isLogicalExpression(expression) && isIdentifierOrLiteral(expression.left) && isIdentifierOrLiteral(expression.right)) { return types.logicalExpression(expression.operator, replace(expression.left), replace(expression.right)); } if (types.isBinaryExpression(expression) && isIdentifierOrLiteral(expression.left) && isIdentifierOrLiteral(expression.right)) { return types.binaryExpression(expression.operator, replace(expression.left), replace(expression.right)); } if (types.isCallExpression(expression) && isIdentifierOrLiteral(expression.callee) && expression.arguments.length === 0) { return types.callExpression(replace(expression.callee), expression.arguments); } } } } } } function awaitAndContinue(state, path, value, continuation, directExpression) { const declarators = []; if (continuation) { if (isPassthroughContinuation(continuation)) { continuation = undefined; } else { continuation = unwrapReturnCallWithPassthroughArgument(continuation, path.scope); } } if (!continuation && directExpression && extractLooseBooleanValue(directExpression) === true) { return { declarators, expression: value, }; } if (types.isCallExpression(value) && value.arguments.length === 0 && isContinuation(value.callee) && value.callee.params.length === 0) { const newValue = expressionInSingleReturnStatement(value.callee); if (newValue) { value = newValue; } } if (continuation && !directExpression && types.isCallExpression(value) && types.isMemberExpression(value.callee) && helperNameMap.get(value.callee) === "_yield") { return { declarators, expression: callThenMethod(value, continuation), }; } if (readConfigKey(state.opts, "inlineHelpers")) { if (directExpression) { const resolvedValue = types.callExpression(promiseResolve(), [value]); const direct = extractLooseBooleanValue(directExpression); if (typeof direct === "undefined") { let expression; if (continuation) { let simpleExpression; if (!types.isIdentifier(continuation) && !(simpleExpression = simpleExpressionForContinuation(continuation, isIdentifierOrLiteral(value) ? value : undefined))) { const id = path.scope.generateUidIdentifier("temp"); if (isContinuation(continuation)) { if (!path.parentPath) { throw path.buildCodeFrameError(`Expected a parent path!`, Error); } insertFunctionIntoScope(continuation, id, path.parentPath.scope); } else { declarators.push(types.variableDeclarator(id, continuation)); } continuation = id; } expression = conditionalExpression(directExpression, simpleExpression || types.callExpression(continuation, [value]), callThenMethod(resolvedValue, continuation)); } else { expression = conditionalExpression(directExpression, value, resolvedValue); } return { declarators, expression, }; } else if (direct) { return { declarators, expression: continuation ? types.callExpression(continuation, [value]) : value, }; } else { return { declarators, expression: continuation ? callThenMethod(resolvedValue, continuation) : resolvedValue, }; } } else if (continuation) { if (!types.isIdentifier(value)) { if (types.isCallExpression(value) && promiseCallExpressionType(value) !== undefined) { return { declarators, expression: callThenMethod(value, continuation), }; } const id = path.scope.generateUidIdentifier("temp"); declarators.push(types.variableDeclarator(id, value)); value = id; } const isEmpty = isEmptyContinuation(continuation); let simpleExpression; if (!isEmpty && !types.isIdentifier(continuation) && !(simpleExpression = simpleExpressionForContinuation(continuation, value))) { const id = path.scope.generateUidIdentifier("temp"); if (isContinuation(continuation)) { if (!path.parentPath) { throw path.buildCodeFrameError(`Expected a parent path!`, Error); } insertFunctionIntoScope(continuation, id, path.parentPath.scope); } else { declarators.push(types.variableDeclarator(id, continuation)); } continuation = id; } return { declarators, expression: types.conditionalExpression(types.logicalExpression("&&", value, types.memberExpression(value, types.identifier("then"))), callThenMethod(value, continuation), simpleExpression ? simpleExpression : isEmpty ? voidExpression() : types.callExpression(continuation, [value])), }; } } const callTarget = types.isCallExpression(value) && value.arguments.length === 0 && !types.isMemberExpression(value.callee) ? value.callee : undefined; const args = [callTarget || value]; const ignoreResult = continuation && isEmptyContinuation(continuation); if (!ignoreResult && continuation) { args.push(continuation); } if (directExpression && extractLooseBooleanValue(directExpression) !== false) { if (!ignoreResult && !continuation) { args.push(voidExpression()); } args.push(directExpression); } const baseHelper = directExpression ? callTarget ? "_call" : "_await" : callTarget ? "_invoke" : "_continue"; const helperName = ignoreResult ? (baseHelper + "Ignored") : baseHelper; if (args.length === 1) { switch (helperName) { case "_invoke": return { declarators, expression: types.callExpression(args[0], []), }; case "_continue": return { declarators, expression: discardingIntrinsics(args[0]), }; case "_continueIgnored": const firstArgument = args[0]; if (types.isCallExpression(firstArgument) && (types.isIdentifier(firstArgument.callee) || types.isMemberExpression(firstArgument.callee))) { if (helperNameMap.get(firstArgument.callee) === "_continueIgnored") { return { declarators, expression: firstArgument, }; } } } } return { declarators, expression: types.callExpression(helperReference(state, path, helperName), args.map(discardingIntrinsics)), }; } function borrowTail(target) { let current = target; const dest = []; while (current && current.node && current.inList && current.container) { const siblings = current.getAllNextSiblings(); for (const sibling of siblings) { sibling.assertStatement(); dest.push(sibling.node); } for (const sibling of siblings) { sibling.remove(); } current = current.parentPath; if (!current || !current.isBlockStatement()) { break; } } return dest; } function exitsInTail(target) { let current = target; while (current && current.node && current.inList && current.container && !current.isFunction()) { for (var i = current.key + 1; i < current.container.length; i++) { if (pathsReturnOrThrow(current).any) { return true; } } current = current.parentPath; } return false; } function returnStatement(argument, originalNode) { const result = types.returnStatement(argument); skipNodeSet.add(result); if (originalNode !== undefined) { originalNodeMap.set(result, originalNode); } return result; } function removeUnnecessaryReturnStatements(blocks) { while (blocks.length) { const lastStatement = blocks[blocks.length - 1]; if (types.isReturnStatement(lastStatement)) { if (lastStatement.argument === null || lastStatement.argument === undefined) { blocks = blocks.slice(0, blocks.length - 1); } else { if (types.isConditionalExpression(lastStatement.argument) && types.isUnaryExpression(lastStatement.argument.alternate) && lastStatement.argument.alternate.operator === "void" && isValueLiteral(lastStatement.argument.alternate.argument)) { blocks = blocks.slice(0, blocks.length - 1); blocks.push(types.ifStatement(lastStatement.argument.test, types.returnStatement(lastStatement.argument.consequent))); } else if (blocks.length > 1) { const previousStatement = blocks[blocks.length - 2]; if (types.isIfStatement(previousStatement) && !previousStatement.alternate) { let consequent = previousStatement.consequent; while (types.isBlockStatement(consequent)) { if (consequent.body.length !== 1) { return blocks; } consequent = consequent.body[0]; } if (types.isReturnStatement(consequent) && consequent.argument) { blocks = blocks.slice(0, blocks.length - 2); blocks.push(types.returnStatement(conditionalExpression(previousStatement.test, consequent.argument, lastStatement.argument))); } } } break; } } else { if (types.isIfStatement(lastStatement)) { let consequent = lastStatement.consequent; if (types.isBlockStatement(consequent)) { consequent = blockStatement(removeUnnecessaryReturnStatements(consequent.body)); } let alternate = lastStatement.alternate; if (alternate) { if (types.isBlockStatement(alternate)) { const removedOfUnnecessary = removeUnnecessaryReturnStatements(alternate.body); alternate = removedOfUnnecessary.length ? blockStatement(removedOfUnnecessary) : undefined; } else if (removeUnnecessaryReturnStatements([alternate]).length === 0) { alternate = undefined; } } if (consequent !== lastStatement.consequent || alternate !== lastStatement.alternate) { blocks = blocks.slice(0, blocks.length - 1); blocks.push(types.ifStatement(lastStatement.test, consequent, alternate || undefined)); } } break; } } return blocks; } function rewriteAsyncNode(state, parentPath, node, additionalConstantNames, exitIdentifier, unpromisify) { const path = pathForNewNode(node, parentPath); rewriteAsyncBlock(state, path, additionalConstantNames, exitIdentifier, unpromisify); return path.node; } function allScopes(scope) { const result = []; while (scope) { result.push(scope); scope = scope.parent; } return result; } const hoistCallArgumentsInnerVisitor = { Identifier(identifierPath) { if (identifierSearchesScope(identifierPath)) { const name = identifierPath.node.name; if (this.argumentNames.indexOf(name) === -1) { if (this.additionalConstantNames.indexOf(name) !== -1) { this.scopes.push(this.path.scope.parent); } else { const binding = identifierPath.scope.getBinding(name) || this.path.scope.getBinding(name); if (binding) { const scope = binding.scope; if (scope !== null) { if (this.pathScopes.indexOf(scope) !== -1) { this.scopes.push(scope); } } } } } } }, }; function isValueLiteral(node) { return types.isStringLiteral(node) || types.isNumericLiteral(node) || types.isBooleanLiteral(node); } function keyFilter(key, value) { return key === "start" || key === "end" || key === "loc" || key === "directives" || key === "leadingComments" || key === "trailingComments" || key === "innerComments" || key[0] === "_" ? undefined : value; } function nodesAreEquivalent(node) { let cached; return (other) => { if (typeof cached === "undefined") { cached = JSON.stringify(node, keyFilter); } return cached === JSON.stringify(other, keyFilter); }; } const reregisterVariableVisitor = { VariableDeclaration(path) { path.scope.registerDeclaration(path); }, FunctionDeclaration(path) { path.parentPath.scope.registerDeclaration(path); }, ClassDeclaration(path) { path.scope.registerDeclaration(path); }, Function(path) { path.skip(); }, }; function insertFunctionIntoScope(func, id, scope) { scope.push({ kind: "const", id, init: func, unique: true }); const binding = scope.getBinding(id.name); if (typeof binding === "undefined") { throw scope.path.buildCodeFrameError(`Could not find newly created binding for ${id.name}!`, Error); } const targetPath = binding.path.parentPath; if (!targetPath) { throw scope.path.buildCodeFrameError(`Could not find newly created binding for ${id.name}!`, Error); } targetPath.replaceWith(types.functionDeclaration(id, func.params, types.isBlockStatement(func.body) ? func.body : types.blockStatement([types.returnStatement(func.body)]), func.generator, func.async)); reregisterDeclarations(targetPath); } function hoistFunctionExpressionHandler(path) { path.skip(); const bodyPath = path.get("body"); if (bodyPath.isBlockStatement() && bodyPath.node.body.length === 0 && !readConfigKey(this.state.opts, "inlineHelpers")) { path.replaceWith(emptyFunction(this.state, path)); return; } const argumentNames = []; for (const param of path.node.params) { if (types.isIdentifier(param) || types.isPattern(param) || types.isRestElement(param)) { addConstantNames(argumentNames, param); } else { return; } } const scopes = []; const pathScopes = allScopes(path.scope.parent); path.traverse(hoistCallArgumentsInnerVisitor, { argumentNames, scopes, pathScopes, path, additionalConstantNames: this.additionalConstantNames, }); let scope = path.scope.getProgramParent(); let ancestry = [scope]; for (let otherScope of scopes) { if (ancestry.indexOf(otherScope) === -1) { scope = otherScope; ancestry = ancestry.concat(allScopes(otherScope)); } } if (ancestry.indexOf(path.scope.parent) === -1) { const bindings = scope.bindings; const filter = nodesAreEquivalent([...path.node.params, path.node.body]); for (const key of Object.getOwnPropertyNames(bindings)) { const binding = bindings[key]; const bindingPath = binding.path; if (bindingPath.isFunctionDeclaration()) { if (filter([...bindingPath.node.params, bindingPath.node.body])) { path.replaceWith(binding.identifier); return; } } else if (bindingPath.isVariableDeclarator()) { const init = bindingPath.get("init"); if (init.node && isContinuation(init.node)) { if (filter([...init.node.params, init.node.body])) { path.replaceWith(binding.identifier); return; } } } } let nameNode = path.node; if (types.isExpression(nameNode) && isContinuation(nameNode)) { nameNode = nameNode.body; } if (types.isBlockStatement(nameNode) && nameNode.body.length === 1) { nameNode = nameNode.body[0]; } if (types.isReturnStatement(nameNode) && nameNode.argument) { nameNode = nameNode.argument; } if (types.isCallExpression(nameNode)) { const callee = nameNode.callee; if (types.isIdentifier(callee) && helperNameMap.has(callee)) { nameNode = nameNode.arguments[0]; } } const id = isValueLiteral(nameNode) ? scope.generateUidIdentifier(nameNode.value.toString().replace(/\d/g, (number) => numberNames[number])) : path.scope.generateUidIdentifierBasedOnNode(nameNode, "temp"); const init = path.node; path.replaceWith(id); insertFunctionIntoScope(init, id, scope); } } const hoistCallArgumentsVisitor = { FunctionExpression: hoistFunctionExpressionHandler, ArrowFunctionExpression: hoistFunctionExpressionHandler, }; function hoistCallArguments(state, path, additionalConstantNames) { if (path.isCallExpression