UNPKG

2.99 kBJavaScriptView Raw
1"use strict";
2
3const utils = require("./utils.js");
4const Visitor = require("./visitor.js");
5
6class AssignmentVisitor extends Visitor {
7 reset(rootPath, options) {
8 this.exportedLocalNames = options.exportedLocalNames;
9 this.magicString = options.magicString;
10 this.modifyAST = !! options.modifyAST;
11 this.moduleAlias = options.moduleAlias;
12
13 if (this.exportedLocalNames === void 0) {
14 this.exportedLocalNames = Object.create(null);
15 }
16
17 if (this.magicString === void 0) {
18 this.magicString = null;
19 }
20
21 if (this.magicString) {
22 const identifiers = Object.keys(this.exportedLocalNames);
23
24 // In addition to any exported local identifiers, this visitor needs
25 // to visit `eval(...)` expressions, so it's important to search for
26 // any possible indexes of the `eval` identifier, too.
27 identifiers.push("eval");
28
29 // When this.possibleIndexes is defined, the AST traversal can
30 // abandon any subtrees that do not contain any possible indexes.
31 this.possibleIndexes = utils.findLikelyIndexes(
32 this.magicString.original,
33 identifiers
34 );
35 }
36 }
37
38 visitAssignmentExpression(path) {
39 return assignmentHelper(this, path, "left");
40 }
41
42 visitCallExpression(path) {
43 const callee = path.getValue().callee;
44
45 if (callee.type === "MemberExpression" &&
46 isId(callee.object, this.moduleAlias) &&
47 isId(callee.property, "runSetters")) {
48 // If we've already wrapped this subtree, abandon it.
49 return false;
50 }
51
52 this.visitChildren(path);
53
54 if (isId(callee, "eval")) {
55 wrap(this, path);
56 }
57 }
58
59 visitUpdateExpression(path) {
60 return assignmentHelper(this, path, "argument");
61 }
62};
63
64function isId(node, name) {
65 if (!node || node.type !== "Identifier") {
66 return false;
67 }
68
69 if (name) {
70 return node.name === name;
71 }
72
73 return true;
74}
75
76function assignmentHelper(visitor, path, childName) {
77 visitor.visitChildren(path);
78
79 const child = path.getValue()[childName];
80 const assignedNames = utils.getNamesFromPattern(child);
81 const nameCount = assignedNames.length;
82
83 // Wrap assignments to exported identifiers with `module.runSetters`.
84 for (let i = 0; i < nameCount; ++i) {
85 if (visitor.exportedLocalNames[assignedNames[i]] === true) {
86 wrap(visitor, path);
87 break;
88 }
89 }
90}
91
92function wrap(visitor, path) {
93 const value = path.getValue();
94
95 if (visitor.magicString !== null) {
96 visitor.magicString.prependRight(
97 value.start,
98 visitor.moduleAlias + ".runSetters("
99 ).appendLeft(value.end, ")");
100 }
101
102 if (visitor.modifyAST) {
103 path.replace({
104 type: "CallExpression",
105 callee: {
106 type: "MemberExpression",
107 object: {
108 type: "Identifier",
109 name: visitor.moduleAlias,
110 },
111 property: {
112 type: "Identifier",
113 name: "runSetters"
114 }
115 },
116 arguments: [value]
117 });
118 }
119}
120
121module.exports = AssignmentVisitor;