"use strict"; const operators = new Set(["+", "-", "*", "%", "<<", ">>", ">>>", "&", "|", "^", "/", "**"]); const updateOperators = new Set(["+", "-"]); module.exports = t => { function simplify(path) { const rightExpr = path.get("right"); const leftExpr = path.get("left"); if (path.node.operator !== "=") { return; } const canBeUpdateExpression = rightExpr.get("right").isNumericLiteral() && rightExpr.get("right").node.value === 1 && updateOperators.has(rightExpr.node.operator); if (leftExpr.isMemberExpression()) { const leftPropNames = getPropNames(leftExpr); const rightPropNames = getPropNames(rightExpr.get("left")); if (!leftPropNames || leftPropNames.indexOf(undefined) > -1 || !rightPropNames || rightPropNames.indexOf(undefined) > -1 || !operators.has(rightExpr.node.operator) || !areArraysEqual(leftPropNames, rightPropNames)) { return; } } else { if (!rightExpr.isBinaryExpression() || !operators.has(rightExpr.node.operator) || leftExpr.node.name !== rightExpr.node.left.name) { return; } } let newExpression; // special case x=x+1 --> ++x if (canBeUpdateExpression) { newExpression = t.updateExpression(rightExpr.node.operator + rightExpr.node.operator, t.clone(leftExpr.node), true /* prefix */ ); } else { newExpression = t.assignmentExpression(rightExpr.node.operator + "=", t.clone(leftExpr.node), t.clone(rightExpr.node.right)); } path.replaceWith(newExpression); } return { simplify }; }; function areArraysEqual(arr1, arr2) { return arr1.every((value, index) => { return String(value) === String(arr2[index]); }); } function getPropNames(path) { if (!path.isMemberExpression()) { return; } let obj = path.get("object"); const prop = path.get("property"); const propNames = [getName(prop.node)]; while (obj.type === "MemberExpression") { const node = obj.get("property").node; if (node) { propNames.push(getName(node)); } obj = obj.get("object"); } propNames.push(getName(obj.node)); return propNames; } function getName(node) { if (node.type === "ThisExpression") { return "this"; } if (node.type === "Super") { return "super"; } if (node.type === "NullLiteral") { return "null"; } // augment identifiers so that they don't match // string/number literals // but still match against each other return node.name ? node.name + "_" : node.value /* Literal */ ; }