UNPKG

12.1 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5var _t = require('@babel/types');
6
7function _interopNamespace(e) {
8 if (e && e.__esModule) return e;
9 var n = Object.create(null);
10 if (e) {
11 Object.keys(e).forEach(function (k) {
12 if (k !== 'default') {
13 var d = Object.getOwnPropertyDescriptor(e, k);
14 Object.defineProperty(n, k, d.get ? d : {
15 enumerable: true,
16 get: function () {
17 return e[k];
18 }
19 });
20 }
21 });
22 }
23 n['default'] = e;
24 return Object.freeze(n);
25}
26
27var _t__namespace = /*#__PURE__*/_interopNamespace(_t);
28
29function willPathCastToBoolean(path) {
30 const maybeWrapped = path;
31 const {
32 node,
33 parentPath
34 } = maybeWrapped;
35
36 if (parentPath.isLogicalExpression()) {
37 const {
38 operator,
39 right
40 } = parentPath.node;
41
42 if (operator === "&&" || operator === "||" || operator === "??" && node === right) {
43 return willPathCastToBoolean(parentPath);
44 }
45 }
46
47 if (parentPath.isSequenceExpression()) {
48 const {
49 expressions
50 } = parentPath.node;
51
52 if (expressions[expressions.length - 1] === node) {
53 return willPathCastToBoolean(parentPath);
54 } else {
55 return true;
56 }
57 }
58
59 return parentPath.isConditional({
60 test: node
61 }) || parentPath.isUnaryExpression({
62 operator: "!"
63 }) || parentPath.isLoop({
64 test: node
65 });
66}
67
68const {
69 LOGICAL_OPERATORS,
70 arrowFunctionExpression,
71 assignmentExpression,
72 binaryExpression,
73 booleanLiteral,
74 callExpression,
75 cloneNode,
76 conditionalExpression,
77 identifier,
78 isMemberExpression,
79 isOptionalCallExpression,
80 isOptionalMemberExpression,
81 isUpdateExpression,
82 logicalExpression,
83 memberExpression,
84 nullLiteral,
85 numericLiteral,
86 optionalCallExpression,
87 optionalMemberExpression,
88 sequenceExpression,
89 unaryExpression
90} = _t__namespace;
91
92class AssignmentMemoiser {
93 constructor() {
94 this._map = void 0;
95 this._map = new WeakMap();
96 }
97
98 has(key) {
99 return this._map.has(key);
100 }
101
102 get(key) {
103 if (!this.has(key)) return;
104
105 const record = this._map.get(key);
106
107 const {
108 value
109 } = record;
110 record.count--;
111
112 if (record.count === 0) {
113 return assignmentExpression("=", value, key);
114 }
115
116 return value;
117 }
118
119 set(key, value, count) {
120 return this._map.set(key, {
121 count,
122 value
123 });
124 }
125
126}
127
128function toNonOptional(path, base) {
129 const {
130 node
131 } = path;
132
133 if (isOptionalMemberExpression(node)) {
134 return memberExpression(base, node.property, node.computed);
135 }
136
137 if (path.isOptionalCallExpression()) {
138 const callee = path.get("callee");
139
140 if (path.node.optional && callee.isOptionalMemberExpression()) {
141 const {
142 object
143 } = callee.node;
144 const context = path.scope.maybeGenerateMemoised(object) || object;
145 callee.get("object").replaceWith(assignmentExpression("=", context, object));
146 return callExpression(memberExpression(base, identifier("call")), [context, ...path.node.arguments]);
147 }
148
149 return callExpression(base, path.node.arguments);
150 }
151
152 return path.node;
153}
154
155function isInDetachedTree(path) {
156 while (path) {
157 if (path.isProgram()) break;
158 const {
159 parentPath,
160 container,
161 listKey
162 } = path;
163 const parentNode = parentPath.node;
164
165 if (listKey) {
166 if (container !== parentNode[listKey]) return true;
167 } else {
168 if (container !== parentNode) return true;
169 }
170
171 path = parentPath;
172 }
173
174 return false;
175}
176
177const handle = {
178 memoise() {},
179
180 handle(member, noDocumentAll) {
181 const {
182 node,
183 parent,
184 parentPath,
185 scope
186 } = member;
187
188 if (member.isOptionalMemberExpression()) {
189 if (isInDetachedTree(member)) return;
190 const endPath = member.find(({
191 node,
192 parent
193 }) => {
194 if (isOptionalMemberExpression(parent)) {
195 return parent.optional || parent.object !== node;
196 }
197
198 if (isOptionalCallExpression(parent)) {
199 return node !== member.node && parent.optional || parent.callee !== node;
200 }
201
202 return true;
203 });
204
205 if (scope.path.isPattern()) {
206 endPath.replaceWith(callExpression(arrowFunctionExpression([], endPath.node), []));
207 return;
208 }
209
210 const willEndPathCastToBoolean = willPathCastToBoolean(endPath);
211 const rootParentPath = endPath.parentPath;
212
213 if (rootParentPath.isUpdateExpression({
214 argument: node
215 }) || rootParentPath.isAssignmentExpression({
216 left: node
217 })) {
218 throw member.buildCodeFrameError(`can't handle assignment`);
219 }
220
221 const isDeleteOperation = rootParentPath.isUnaryExpression({
222 operator: "delete"
223 });
224
225 if (isDeleteOperation && endPath.isOptionalMemberExpression() && endPath.get("property").isPrivateName()) {
226 throw member.buildCodeFrameError(`can't delete a private class element`);
227 }
228
229 let startingOptional = member;
230
231 for (;;) {
232 if (startingOptional.isOptionalMemberExpression()) {
233 if (startingOptional.node.optional) break;
234 startingOptional = startingOptional.get("object");
235 continue;
236 } else if (startingOptional.isOptionalCallExpression()) {
237 if (startingOptional.node.optional) break;
238 startingOptional = startingOptional.get("callee");
239 continue;
240 }
241
242 throw new Error(`Internal error: unexpected ${startingOptional.node.type}`);
243 }
244
245 const startingProp = startingOptional.isOptionalMemberExpression() ? "object" : "callee";
246 const startingNode = startingOptional.node[startingProp];
247 const baseNeedsMemoised = scope.maybeGenerateMemoised(startingNode);
248 const baseRef = baseNeedsMemoised != null ? baseNeedsMemoised : startingNode;
249 const parentIsOptionalCall = parentPath.isOptionalCallExpression({
250 callee: node
251 });
252
253 const isOptionalCall = parent => parentIsOptionalCall;
254
255 const parentIsCall = parentPath.isCallExpression({
256 callee: node
257 });
258 startingOptional.replaceWith(toNonOptional(startingOptional, baseRef));
259
260 if (isOptionalCall()) {
261 if (parent.optional) {
262 parentPath.replaceWith(this.optionalCall(member, parent.arguments));
263 } else {
264 parentPath.replaceWith(this.call(member, parent.arguments));
265 }
266 } else if (parentIsCall) {
267 member.replaceWith(this.boundGet(member));
268 } else {
269 member.replaceWith(this.get(member));
270 }
271
272 let regular = member.node;
273
274 for (let current = member; current !== endPath;) {
275 const parentPath = current.parentPath;
276
277 if (parentPath === endPath && isOptionalCall() && parent.optional) {
278 regular = parentPath.node;
279 break;
280 }
281
282 regular = toNonOptional(parentPath, regular);
283 current = parentPath;
284 }
285
286 let context;
287 const endParentPath = endPath.parentPath;
288
289 if (isMemberExpression(regular) && endParentPath.isOptionalCallExpression({
290 callee: endPath.node,
291 optional: true
292 })) {
293 const {
294 object
295 } = regular;
296 context = member.scope.maybeGenerateMemoised(object);
297
298 if (context) {
299 regular.object = assignmentExpression("=", context, object);
300 }
301 }
302
303 let replacementPath = endPath;
304
305 if (isDeleteOperation) {
306 replacementPath = endParentPath;
307 regular = endParentPath.node;
308 }
309
310 const baseMemoised = baseNeedsMemoised ? assignmentExpression("=", cloneNode(baseRef), cloneNode(startingNode)) : cloneNode(baseRef);
311
312 if (willEndPathCastToBoolean) {
313 let nonNullishCheck;
314
315 if (noDocumentAll) {
316 nonNullishCheck = binaryExpression("!=", baseMemoised, nullLiteral());
317 } else {
318 nonNullishCheck = logicalExpression("&&", binaryExpression("!==", baseMemoised, nullLiteral()), binaryExpression("!==", cloneNode(baseRef), scope.buildUndefinedNode()));
319 }
320
321 replacementPath.replaceWith(logicalExpression("&&", nonNullishCheck, regular));
322 } else {
323 let nullishCheck;
324
325 if (noDocumentAll) {
326 nullishCheck = binaryExpression("==", baseMemoised, nullLiteral());
327 } else {
328 nullishCheck = logicalExpression("||", binaryExpression("===", baseMemoised, nullLiteral()), binaryExpression("===", cloneNode(baseRef), scope.buildUndefinedNode()));
329 }
330
331 replacementPath.replaceWith(conditionalExpression(nullishCheck, isDeleteOperation ? booleanLiteral(true) : scope.buildUndefinedNode(), regular));
332 }
333
334 if (context) {
335 const endParent = endParentPath.node;
336 endParentPath.replaceWith(optionalCallExpression(optionalMemberExpression(endParent.callee, identifier("call"), false, true), [cloneNode(context), ...endParent.arguments], false));
337 }
338
339 return;
340 }
341
342 if (isUpdateExpression(parent, {
343 argument: node
344 })) {
345 if (this.simpleSet) {
346 member.replaceWith(this.simpleSet(member));
347 return;
348 }
349
350 const {
351 operator,
352 prefix
353 } = parent;
354 this.memoise(member, 2);
355 const value = binaryExpression(operator[0], unaryExpression("+", this.get(member)), numericLiteral(1));
356
357 if (prefix) {
358 parentPath.replaceWith(this.set(member, value));
359 } else {
360 const {
361 scope
362 } = member;
363 const ref = scope.generateUidIdentifierBasedOnNode(node);
364 scope.push({
365 id: ref
366 });
367 value.left = assignmentExpression("=", cloneNode(ref), value.left);
368 parentPath.replaceWith(sequenceExpression([this.set(member, value), cloneNode(ref)]));
369 }
370
371 return;
372 }
373
374 if (parentPath.isAssignmentExpression({
375 left: node
376 })) {
377 if (this.simpleSet) {
378 member.replaceWith(this.simpleSet(member));
379 return;
380 }
381
382 const {
383 operator,
384 right: value
385 } = parentPath.node;
386
387 if (operator === "=") {
388 parentPath.replaceWith(this.set(member, value));
389 } else {
390 const operatorTrunc = operator.slice(0, -1);
391
392 if (LOGICAL_OPERATORS.includes(operatorTrunc)) {
393 this.memoise(member, 1);
394 parentPath.replaceWith(logicalExpression(operatorTrunc, this.get(member), this.set(member, value)));
395 } else {
396 this.memoise(member, 2);
397 parentPath.replaceWith(this.set(member, binaryExpression(operatorTrunc, this.get(member), value)));
398 }
399 }
400
401 return;
402 }
403
404 if (parentPath.isCallExpression({
405 callee: node
406 })) {
407 parentPath.replaceWith(this.call(member, parentPath.node.arguments));
408 return;
409 }
410
411 if (parentPath.isOptionalCallExpression({
412 callee: node
413 })) {
414 if (scope.path.isPattern()) {
415 parentPath.replaceWith(callExpression(arrowFunctionExpression([], parentPath.node), []));
416 return;
417 }
418
419 parentPath.replaceWith(this.optionalCall(member, parentPath.node.arguments));
420 return;
421 }
422
423 if (parentPath.isForXStatement({
424 left: node
425 }) || parentPath.isObjectProperty({
426 value: node
427 }) && parentPath.parentPath.isObjectPattern() || parentPath.isAssignmentPattern({
428 left: node
429 }) && parentPath.parentPath.isObjectProperty({
430 value: parent
431 }) && parentPath.parentPath.parentPath.isObjectPattern() || parentPath.isArrayPattern() || parentPath.isAssignmentPattern({
432 left: node
433 }) && parentPath.parentPath.isArrayPattern() || parentPath.isRestElement()) {
434 member.replaceWith(this.destructureSet(member));
435 return;
436 }
437
438 if (parentPath.isTaggedTemplateExpression()) {
439 member.replaceWith(this.boundGet(member));
440 } else {
441 member.replaceWith(this.get(member));
442 }
443 }
444
445};
446function memberExpressionToFunctions(path, visitor, state) {
447 path.traverse(visitor, Object.assign({}, handle, state, {
448 memoiser: new AssignmentMemoiser()
449 }));
450}
451
452exports.default = memberExpressionToFunctions;
453//# sourceMappingURL=index.js.map