/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at . */ import { replaceNode } from "./utils/ast"; import { isTopLevel } from "./utils/helpers"; import generate from "@babel/generator"; import * as t from "@babel/types"; function getAssignmentTarget(node, bindings) { if (t.isObjectPattern(node)) { for (const property of node.properties) { if (t.isRestElement(property)) { property.argument = getAssignmentTarget(property.argument, bindings); } else { property.value = getAssignmentTarget(property.value, bindings); } } return node; } if (t.isArrayPattern(node)) { for (const [i, element] of node.elements.entries()) { node.elements[i] = getAssignmentTarget(element, bindings); } return node; } if (t.isAssignmentPattern(node)) { node.left = getAssignmentTarget(node.left, bindings); return node; } if (t.isRestElement(node)) { node.argument = getAssignmentTarget(node.argument, bindings); return node; } if (t.isIdentifier(node)) { return bindings.includes(node.name) ? node : t.memberExpression(t.identifier("self"), node); } return node; } // translates new bindings `var a = 3` into `self.a = 3` // and existing bindings `var a = 3` into `a = 3` for re-assignments function globalizeDeclaration(node, bindings) { return node.declarations.map(declaration => t.expressionStatement( t.assignmentExpression( "=", getAssignmentTarget(declaration.id, bindings), declaration.init || t.unaryExpression("void", t.numericLiteral(0)) ) ) ); } // translates new bindings `a = 3` into `self.a = 3` // and keeps assignments the same for existing bindings. function globalizeAssignment(node, bindings) { return t.assignmentExpression( node.operator, getAssignmentTarget(node.left, bindings), node.right ); } export default function mapExpressionBindings(expression, ast, bindings = []) { let isMapped = false; let shouldUpdate = true; t.traverse(ast, (node, ancestors) => { const parent = ancestors[ancestors.length - 1]; if (t.isWithStatement(node)) { shouldUpdate = false; return; } if (!isTopLevel(ancestors)) { return; } if (t.isAssignmentExpression(node)) { if (t.isIdentifier(node.left) || t.isPattern(node.left)) { const newNode = globalizeAssignment(node, bindings); isMapped = true; replaceNode(ancestors, newNode); return; } return; } if (!t.isVariableDeclaration(node)) { return; } if (!t.isForStatement(parent.node)) { const newNodes = globalizeDeclaration(node, bindings); isMapped = true; replaceNode(ancestors, newNodes); } }); if (!shouldUpdate || !isMapped) { return expression; } return generate(ast).code; }