babel-plugin-transform-async-to-promises
Version:
Transform async/await to promise chains
1,083 lines • 187 kB
JavaScript
"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