mapBindings.js (3028B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ 4 5 import { replaceNode } from "./utils/ast"; 6 import { isTopLevel } from "./utils/helpers"; 7 8 import generate from "@babel/generator"; 9 import * as t from "@babel/types"; 10 11 function getAssignmentTarget(node, bindings) { 12 if (t.isObjectPattern(node)) { 13 for (const property of node.properties) { 14 if (t.isRestElement(property)) { 15 property.argument = getAssignmentTarget(property.argument, bindings); 16 } else { 17 property.value = getAssignmentTarget(property.value, bindings); 18 } 19 } 20 21 return node; 22 } 23 24 if (t.isArrayPattern(node)) { 25 for (const [i, element] of node.elements.entries()) { 26 node.elements[i] = getAssignmentTarget(element, bindings); 27 } 28 29 return node; 30 } 31 32 if (t.isAssignmentPattern(node)) { 33 node.left = getAssignmentTarget(node.left, bindings); 34 35 return node; 36 } 37 38 if (t.isRestElement(node)) { 39 node.argument = getAssignmentTarget(node.argument, bindings); 40 41 return node; 42 } 43 44 if (t.isIdentifier(node)) { 45 return bindings.includes(node.name) 46 ? node 47 : t.memberExpression(t.identifier("self"), node); 48 } 49 50 return node; 51 } 52 53 // translates new bindings `var a = 3` into `self.a = 3` 54 // and existing bindings `var a = 3` into `a = 3` for re-assignments 55 function globalizeDeclaration(node, bindings) { 56 return node.declarations.map(declaration => 57 t.expressionStatement( 58 t.assignmentExpression( 59 "=", 60 getAssignmentTarget(declaration.id, bindings), 61 declaration.init || t.unaryExpression("void", t.numericLiteral(0)) 62 ) 63 ) 64 ); 65 } 66 67 // translates new bindings `a = 3` into `self.a = 3` 68 // and keeps assignments the same for existing bindings. 69 function globalizeAssignment(node, bindings) { 70 return t.assignmentExpression( 71 node.operator, 72 getAssignmentTarget(node.left, bindings), 73 node.right 74 ); 75 } 76 77 export default function mapExpressionBindings(expression, ast, bindings = []) { 78 let isMapped = false; 79 let shouldUpdate = true; 80 81 t.traverse(ast, (node, ancestors) => { 82 const parent = ancestors[ancestors.length - 1]; 83 84 if (t.isWithStatement(node)) { 85 shouldUpdate = false; 86 return; 87 } 88 89 if (!isTopLevel(ancestors)) { 90 return; 91 } 92 93 if (t.isAssignmentExpression(node)) { 94 if (t.isIdentifier(node.left) || t.isPattern(node.left)) { 95 const newNode = globalizeAssignment(node, bindings); 96 isMapped = true; 97 replaceNode(ancestors, newNode); 98 return; 99 } 100 101 return; 102 } 103 104 if (!t.isVariableDeclaration(node)) { 105 return; 106 } 107 108 if (!t.isForStatement(parent.node)) { 109 const newNodes = globalizeDeclaration(node, bindings); 110 isMapped = true; 111 replaceNode(ancestors, newNodes); 112 } 113 }); 114 115 if (!shouldUpdate || !isMapped) { 116 return expression; 117 } 118 119 return generate(ast).code; 120 }