forked from firefox-devtools/debugger
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmapBindings.js
More file actions
110 lines (89 loc) · 2.85 KB
/
mapBindings.js
File metadata and controls
110 lines (89 loc) · 2.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/* 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 <http://mozilla.org/MPL/2.0/>. */
// @flow
import { parseScript } from "./utils/ast";
import { isTopLevel } from "./utils/helpers";
import generate from "@babel/generator";
import * as t from "@babel/types";
// 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 => {
const identifier = bindings.includes(declaration.id.name)
? declaration.id
: t.memberExpression(t.identifier("self"), declaration.id);
return t.expressionStatement(
t.assignmentExpression("=", identifier, declaration.init)
);
});
}
// translates new bindings `a = 3` into `self.a = 3`
// and keeps assignments the same for existing bindings.
function globalizeAssignment(node, bindings) {
if (bindings.includes(node.left.name)) {
return node;
}
const identifier = t.memberExpression(t.identifier("self"), node.left);
return t.assignmentExpression(node.operator, identifier, node.right);
}
function replaceNode(ancestors, node) {
const parent = ancestors[ancestors.length - 1];
if (typeof parent.index === "number") {
if (Array.isArray(node)) {
parent.node[parent.key].splice(parent.index, 1, ...node);
} else {
parent.node[parent.key][parent.index] = node;
}
} else {
parent.node[parent.key] = node;
}
}
function hasDestructuring(node) {
return node.declarations.some(declaration => t.isPattern(declaration.id));
}
export default function mapExpressionBindings(
expression: string,
bindings: string[] = []
): string {
const ast = parseScript(expression, { allowAwaitOutsideFunction: true });
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)) {
const newNode = globalizeAssignment(node, bindings);
isMapped = true;
return replaceNode(ancestors, newNode);
}
if (t.isPattern(node.left)) {
shouldUpdate = false;
return;
}
}
if (!t.isVariableDeclaration(node)) {
return;
}
if (hasDestructuring(node)) {
shouldUpdate = false;
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;
}