UNPKG

12.3 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 () { return e[k]; }
17 });
18 }
19 });
20 }
21 n["default"] = e;
22 return Object.freeze(n);
23}
24
25var _t__namespace = /*#__PURE__*/_interopNamespace(_t);
26
27function willPathCastToBoolean(path) {
28 const maybeWrapped = path;
29 const {
30 node,
31 parentPath
32 } = maybeWrapped;
33
34 if (parentPath.isLogicalExpression()) {
35 const {
36 operator,
37 right
38 } = parentPath.node;
39
40 if (operator === "&&" || operator === "||" || operator === "??" && node === right) {
41 return willPathCastToBoolean(parentPath);
42 }
43 }
44
45 if (parentPath.isSequenceExpression()) {
46 const {
47 expressions
48 } = parentPath.node;
49
50 if (expressions[expressions.length - 1] === node) {
51 return willPathCastToBoolean(parentPath);
52 } else {
53 return true;
54 }
55 }
56
57 return parentPath.isConditional({
58 test: node
59 }) || parentPath.isUnaryExpression({
60 operator: "!"
61 }) || parentPath.isLoop({
62 test: node
63 });
64}
65
66const {
67 LOGICAL_OPERATORS,
68 arrowFunctionExpression,
69 assignmentExpression,
70 binaryExpression,
71 booleanLiteral,
72 callExpression,
73 cloneNode,
74 conditionalExpression,
75 identifier,
76 isMemberExpression,
77 isOptionalCallExpression,
78 isOptionalMemberExpression,
79 isUpdateExpression,
80 logicalExpression,
81 memberExpression,
82 nullLiteral,
83 optionalCallExpression,
84 optionalMemberExpression,
85 sequenceExpression,
86 updateExpression
87} = _t__namespace;
88
89class AssignmentMemoiser {
90 constructor() {
91 this._map = void 0;
92 this._map = new WeakMap();
93 }
94
95 has(key) {
96 return this._map.has(key);
97 }
98
99 get(key) {
100 if (!this.has(key)) return;
101
102 const record = this._map.get(key);
103
104 const {
105 value
106 } = record;
107 record.count--;
108
109 if (record.count === 0) {
110 return assignmentExpression("=", value, key);
111 }
112
113 return value;
114 }
115
116 set(key, value, count) {
117 return this._map.set(key, {
118 count,
119 value
120 });
121 }
122
123}
124
125function toNonOptional(path, base) {
126 const {
127 node
128 } = path;
129
130 if (isOptionalMemberExpression(node)) {
131 return memberExpression(base, node.property, node.computed);
132 }
133
134 if (path.isOptionalCallExpression()) {
135 const callee = path.get("callee");
136
137 if (path.node.optional && callee.isOptionalMemberExpression()) {
138 const object = callee.node.object;
139 const context = path.scope.maybeGenerateMemoised(object);
140 callee.get("object").replaceWith(assignmentExpression("=", context, object));
141 return callExpression(memberExpression(base, identifier("call")), [context, ...path.node.arguments]);
142 }
143
144 return callExpression(base, path.node.arguments);
145 }
146
147 return path.node;
148}
149
150function isInDetachedTree(path) {
151 while (path) {
152 if (path.isProgram()) break;
153 const {
154 parentPath,
155 container,
156 listKey
157 } = path;
158 const parentNode = parentPath.node;
159
160 if (listKey) {
161 if (container !== parentNode[listKey]) {
162 return true;
163 }
164 } else {
165 if (container !== parentNode) return true;
166 }
167
168 path = parentPath;
169 }
170
171 return false;
172}
173
174const handle = {
175 memoise() {},
176
177 handle(member, noDocumentAll) {
178 const {
179 node,
180 parent,
181 parentPath,
182 scope
183 } = member;
184
185 if (member.isOptionalMemberExpression()) {
186 if (isInDetachedTree(member)) return;
187 const endPath = member.find(({
188 node,
189 parent
190 }) => {
191 if (isOptionalMemberExpression(parent)) {
192 return parent.optional || parent.object !== node;
193 }
194
195 if (isOptionalCallExpression(parent)) {
196 return node !== member.node && parent.optional || parent.callee !== node;
197 }
198
199 return true;
200 });
201
202 if (scope.path.isPattern()) {
203 endPath.replaceWith(callExpression(arrowFunctionExpression([], endPath.node), []));
204 return;
205 }
206
207 const willEndPathCastToBoolean = willPathCastToBoolean(endPath);
208 const rootParentPath = endPath.parentPath;
209
210 if (rootParentPath.isUpdateExpression({
211 argument: node
212 }) || rootParentPath.isAssignmentExpression({
213 left: node
214 })) {
215 throw member.buildCodeFrameError(`can't handle assignment`);
216 }
217
218 const isDeleteOperation = rootParentPath.isUnaryExpression({
219 operator: "delete"
220 });
221
222 if (isDeleteOperation && endPath.isOptionalMemberExpression() && endPath.get("property").isPrivateName()) {
223 throw member.buildCodeFrameError(`can't delete a private class element`);
224 }
225
226 let startingOptional = member;
227
228 for (;;) {
229 if (startingOptional.isOptionalMemberExpression()) {
230 if (startingOptional.node.optional) break;
231 startingOptional = startingOptional.get("object");
232 continue;
233 } else if (startingOptional.isOptionalCallExpression()) {
234 if (startingOptional.node.optional) break;
235 startingOptional = startingOptional.get("callee");
236 continue;
237 }
238
239 throw new Error(`Internal error: unexpected ${startingOptional.node.type}`);
240 }
241
242 const startingNode = startingOptional.isOptionalMemberExpression() ? startingOptional.node.object : startingOptional.node.callee;
243 const baseNeedsMemoised = scope.maybeGenerateMemoised(startingNode);
244 const baseRef = baseNeedsMemoised != null ? baseNeedsMemoised : startingNode;
245 const parentIsOptionalCall = parentPath.isOptionalCallExpression({
246 callee: node
247 });
248
249 const isOptionalCall = parent => parentIsOptionalCall;
250
251 const parentIsCall = parentPath.isCallExpression({
252 callee: node
253 });
254 startingOptional.replaceWith(toNonOptional(startingOptional, baseRef));
255
256 if (isOptionalCall()) {
257 if (parent.optional) {
258 parentPath.replaceWith(this.optionalCall(member, parent.arguments));
259 } else {
260 parentPath.replaceWith(this.call(member, parent.arguments));
261 }
262 } else if (parentIsCall) {
263 member.replaceWith(this.boundGet(member));
264 } else {
265 member.replaceWith(this.get(member));
266 }
267
268 let regular = member.node;
269
270 for (let current = member; current !== endPath;) {
271 const parentPath = current.parentPath;
272
273 if (parentPath === endPath && isOptionalCall() && parent.optional) {
274 regular = parentPath.node;
275 break;
276 }
277
278 regular = toNonOptional(parentPath, regular);
279 current = parentPath;
280 }
281
282 let context;
283 const endParentPath = endPath.parentPath;
284
285 if (isMemberExpression(regular) && endParentPath.isOptionalCallExpression({
286 callee: endPath.node,
287 optional: true
288 })) {
289 const {
290 object
291 } = regular;
292 context = member.scope.maybeGenerateMemoised(object);
293
294 if (context) {
295 regular.object = assignmentExpression("=", context, object);
296 }
297 }
298
299 let replacementPath = endPath;
300
301 if (isDeleteOperation) {
302 replacementPath = endParentPath;
303 regular = endParentPath.node;
304 }
305
306 const baseMemoised = baseNeedsMemoised ? assignmentExpression("=", cloneNode(baseRef), cloneNode(startingNode)) : cloneNode(baseRef);
307
308 if (willEndPathCastToBoolean) {
309 let nonNullishCheck;
310
311 if (noDocumentAll) {
312 nonNullishCheck = binaryExpression("!=", baseMemoised, nullLiteral());
313 } else {
314 nonNullishCheck = logicalExpression("&&", binaryExpression("!==", baseMemoised, nullLiteral()), binaryExpression("!==", cloneNode(baseRef), scope.buildUndefinedNode()));
315 }
316
317 replacementPath.replaceWith(logicalExpression("&&", nonNullishCheck, regular));
318 } else {
319 let nullishCheck;
320
321 if (noDocumentAll) {
322 nullishCheck = binaryExpression("==", baseMemoised, nullLiteral());
323 } else {
324 nullishCheck = logicalExpression("||", binaryExpression("===", baseMemoised, nullLiteral()), binaryExpression("===", cloneNode(baseRef), scope.buildUndefinedNode()));
325 }
326
327 replacementPath.replaceWith(conditionalExpression(nullishCheck, isDeleteOperation ? booleanLiteral(true) : scope.buildUndefinedNode(), regular));
328 }
329
330 if (context) {
331 const endParent = endParentPath.node;
332 endParentPath.replaceWith(optionalCallExpression(optionalMemberExpression(endParent.callee, identifier("call"), false, true), [cloneNode(context), ...endParent.arguments], false));
333 }
334
335 return;
336 }
337
338 if (isUpdateExpression(parent, {
339 argument: node
340 })) {
341 if (this.simpleSet) {
342 member.replaceWith(this.simpleSet(member));
343 return;
344 }
345
346 const {
347 operator,
348 prefix
349 } = parent;
350 this.memoise(member, 2);
351 const ref = scope.generateUidIdentifierBasedOnNode(node);
352 scope.push({
353 id: ref
354 });
355 const seq = [assignmentExpression("=", cloneNode(ref), this.get(member))];
356
357 if (prefix) {
358 seq.push(updateExpression(operator, cloneNode(ref), prefix));
359 const value = sequenceExpression(seq);
360 parentPath.replaceWith(this.set(member, value));
361 return;
362 } else {
363 const ref2 = scope.generateUidIdentifierBasedOnNode(node);
364 scope.push({
365 id: ref2
366 });
367 seq.push(assignmentExpression("=", cloneNode(ref2), updateExpression(operator, cloneNode(ref), prefix)), cloneNode(ref));
368 const value = sequenceExpression(seq);
369 parentPath.replaceWith(sequenceExpression([this.set(member, value), cloneNode(ref2)]));
370 return;
371 }
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