UNPKG

8.44 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5var helperPluginUtils = require('@babel/helper-plugin-utils');
6var helperSkipTransparentExpressionWrappers = require('@babel/helper-skip-transparent-expression-wrappers');
7var syntaxOptionalChaining = require('@babel/plugin-syntax-optional-chaining');
8var core = require('@babel/core');
9
10function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
11
12var syntaxOptionalChaining__default = /*#__PURE__*/_interopDefaultLegacy(syntaxOptionalChaining);
13
14function willPathCastToBoolean(path) {
15 const maybeWrapped = findOutermostTransparentParent(path);
16 const {
17 node,
18 parentPath
19 } = maybeWrapped;
20
21 if (parentPath.isLogicalExpression()) {
22 const {
23 operator,
24 right
25 } = parentPath.node;
26
27 if (operator === "&&" || operator === "||" || operator === "??" && node === right) {
28 return willPathCastToBoolean(parentPath);
29 }
30 }
31
32 if (parentPath.isSequenceExpression()) {
33 const {
34 expressions
35 } = parentPath.node;
36
37 if (expressions[expressions.length - 1] === node) {
38 return willPathCastToBoolean(parentPath);
39 } else {
40 return true;
41 }
42 }
43
44 return parentPath.isConditional({
45 test: node
46 }) || parentPath.isUnaryExpression({
47 operator: "!"
48 }) || parentPath.isLoop({
49 test: node
50 });
51}
52function findOutermostTransparentParent(path) {
53 let maybeWrapped = path;
54 path.findParent(p => {
55 if (!helperSkipTransparentExpressionWrappers.isTransparentExprWrapper(p)) return true;
56 maybeWrapped = p;
57 });
58 return maybeWrapped;
59}
60
61const {
62 ast
63} = core.template.expression;
64var index = helperPluginUtils.declare((api, options) => {
65 api.assertVersion(7);
66 const {
67 loose = false
68 } = options;
69
70 function isSimpleMemberExpression(expression) {
71 expression = helperSkipTransparentExpressionWrappers.skipTransparentExprWrappers(expression);
72 return core.types.isIdentifier(expression) || core.types.isSuper(expression) || core.types.isMemberExpression(expression) && !expression.computed && isSimpleMemberExpression(expression.object);
73 }
74
75 function needsMemoize(path) {
76 let optionalPath = path;
77 const {
78 scope
79 } = path;
80
81 while (optionalPath.isOptionalMemberExpression() || optionalPath.isOptionalCallExpression()) {
82 const {
83 node
84 } = optionalPath;
85 const childKey = optionalPath.isOptionalMemberExpression() ? "object" : "callee";
86 const childPath = helperSkipTransparentExpressionWrappers.skipTransparentExprWrappers(optionalPath.get(childKey));
87
88 if (node.optional) {
89 return !scope.isStatic(childPath.node);
90 }
91
92 optionalPath = childPath;
93 }
94 }
95
96 return {
97 name: "proposal-optional-chaining",
98 inherits: syntaxOptionalChaining__default['default'],
99 visitor: {
100 "OptionalCallExpression|OptionalMemberExpression"(path) {
101 const {
102 scope
103 } = path;
104 const maybeWrapped = findOutermostTransparentParent(path);
105 const {
106 parentPath
107 } = maybeWrapped;
108 const willReplacementCastToBoolean = willPathCastToBoolean(maybeWrapped);
109 let isDeleteOperation = false;
110 const parentIsCall = parentPath.isCallExpression({
111 callee: maybeWrapped.node
112 }) && path.isOptionalMemberExpression();
113 const optionals = [];
114 let optionalPath = path;
115
116 if (scope.path.isPattern() && needsMemoize(optionalPath)) {
117 path.replaceWith(core.template.ast`(() => ${path.node})()`);
118 return;
119 }
120
121 while (optionalPath.isOptionalMemberExpression() || optionalPath.isOptionalCallExpression()) {
122 const {
123 node
124 } = optionalPath;
125
126 if (node.optional) {
127 optionals.push(node);
128 }
129
130 if (optionalPath.isOptionalMemberExpression()) {
131 optionalPath.node.type = "MemberExpression";
132 optionalPath = helperSkipTransparentExpressionWrappers.skipTransparentExprWrappers(optionalPath.get("object"));
133 } else if (optionalPath.isOptionalCallExpression()) {
134 optionalPath.node.type = "CallExpression";
135 optionalPath = helperSkipTransparentExpressionWrappers.skipTransparentExprWrappers(optionalPath.get("callee"));
136 }
137 }
138
139 let replacementPath = path;
140
141 if (parentPath.isUnaryExpression({
142 operator: "delete"
143 })) {
144 replacementPath = parentPath;
145 isDeleteOperation = true;
146 }
147
148 for (let i = optionals.length - 1; i >= 0; i--) {
149 const node = optionals[i];
150 const isCall = core.types.isCallExpression(node);
151 const replaceKey = isCall ? "callee" : "object";
152 const chainWithTypes = node[replaceKey];
153 let chain = chainWithTypes;
154
155 while (helperSkipTransparentExpressionWrappers.isTransparentExprWrapper(chain)) {
156 chain = chain.expression;
157 }
158
159 let ref;
160 let check;
161
162 if (isCall && core.types.isIdentifier(chain, {
163 name: "eval"
164 })) {
165 check = ref = chain;
166 node[replaceKey] = core.types.sequenceExpression([core.types.numericLiteral(0), ref]);
167 } else if (loose && isCall && isSimpleMemberExpression(chain)) {
168 check = ref = chainWithTypes;
169 } else {
170 ref = scope.maybeGenerateMemoised(chain);
171
172 if (ref) {
173 check = core.types.assignmentExpression("=", core.types.cloneNode(ref), chainWithTypes);
174 node[replaceKey] = ref;
175 } else {
176 check = ref = chainWithTypes;
177 }
178 }
179
180 if (isCall && core.types.isMemberExpression(chain)) {
181 if (loose && isSimpleMemberExpression(chain)) {
182 node.callee = chainWithTypes;
183 } else {
184 const {
185 object
186 } = chain;
187 let context = scope.maybeGenerateMemoised(object);
188
189 if (context) {
190 chain.object = core.types.assignmentExpression("=", context, object);
191 } else if (core.types.isSuper(object)) {
192 context = core.types.thisExpression();
193 } else {
194 context = object;
195 }
196
197 node.arguments.unshift(core.types.cloneNode(context));
198 node.callee = core.types.memberExpression(node.callee, core.types.identifier("call"));
199 }
200 }
201
202 let replacement = replacementPath.node;
203
204 if (i === 0 && parentIsCall) {
205 var _baseRef;
206
207 const object = helperSkipTransparentExpressionWrappers.skipTransparentExprWrappers(replacementPath.get("object")).node;
208 let baseRef;
209
210 if (!loose || !isSimpleMemberExpression(object)) {
211 baseRef = scope.maybeGenerateMemoised(object);
212
213 if (baseRef) {
214 replacement.object = core.types.assignmentExpression("=", baseRef, object);
215 }
216 }
217
218 replacement = core.types.callExpression(core.types.memberExpression(replacement, core.types.identifier("bind")), [core.types.cloneNode((_baseRef = baseRef) != null ? _baseRef : object)]);
219 }
220
221 if (willReplacementCastToBoolean) {
222 const nonNullishCheck = loose ? ast`${core.types.cloneNode(check)} != null` : ast`
223 ${core.types.cloneNode(check)} !== null && ${core.types.cloneNode(ref)} !== void 0`;
224 replacementPath.replaceWith(core.types.logicalExpression("&&", nonNullishCheck, replacement));
225 replacementPath = helperSkipTransparentExpressionWrappers.skipTransparentExprWrappers(replacementPath.get("right"));
226 } else {
227 const nullishCheck = loose ? ast`${core.types.cloneNode(check)} == null` : ast`
228 ${core.types.cloneNode(check)} === null || ${core.types.cloneNode(ref)} === void 0`;
229 const returnValue = isDeleteOperation ? ast`true` : ast`void 0`;
230 replacementPath.replaceWith(core.types.conditionalExpression(nullishCheck, returnValue, replacement));
231 replacementPath = helperSkipTransparentExpressionWrappers.skipTransparentExprWrappers(replacementPath.get("alternate"));
232 }
233 }
234 }
235
236 }
237 };
238});
239
240exports.default = index;
241//# sourceMappingURL=index.js.map