UNPKG

14.3 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.toComputedKey = toComputedKey;
7exports.ensureBlock = ensureBlock;
8exports.arrowFunctionToShadowed = arrowFunctionToShadowed;
9exports.unwrapFunctionEnvironment = unwrapFunctionEnvironment;
10exports.arrowFunctionToExpression = arrowFunctionToExpression;
11
12var t = _interopRequireWildcard(require("@babel/types"));
13
14var _helperFunctionName = _interopRequireDefault(require("@babel/helper-function-name"));
15
16function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
18function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
19
20function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
21
22function toComputedKey() {
23 const node = this.node;
24 let key;
25
26 if (this.isMemberExpression()) {
27 key = node.property;
28 } else if (this.isProperty() || this.isMethod()) {
29 key = node.key;
30 } else {
31 throw new ReferenceError("todo");
32 }
33
34 if (!node.computed) {
35 if (t.isIdentifier(key)) key = t.stringLiteral(key.name);
36 }
37
38 return key;
39}
40
41function ensureBlock() {
42 const body = this.get("body");
43 const bodyNode = body.node;
44
45 if (Array.isArray(body)) {
46 throw new Error("Can't convert array path to a block statement");
47 }
48
49 if (!bodyNode) {
50 throw new Error("Can't convert node without a body");
51 }
52
53 if (body.isBlockStatement()) {
54 return bodyNode;
55 }
56
57 const statements = [];
58 let stringPath = "body";
59 let key;
60 let listKey;
61
62 if (body.isStatement()) {
63 listKey = "body";
64 key = 0;
65 statements.push(body.node);
66 } else {
67 stringPath += ".body.0";
68
69 if (this.isFunction()) {
70 key = "argument";
71 statements.push(t.returnStatement(body.node));
72 } else {
73 key = "expression";
74 statements.push(t.expressionStatement(body.node));
75 }
76 }
77
78 this.node.body = t.blockStatement(statements);
79 const parentPath = this.get(stringPath);
80 body.setup(parentPath, listKey ? parentPath.node[listKey] : parentPath.node, listKey, key);
81 return this.node;
82}
83
84function arrowFunctionToShadowed() {
85 if (!this.isArrowFunctionExpression()) return;
86 this.arrowFunctionToExpression();
87}
88
89function unwrapFunctionEnvironment() {
90 if (!this.isArrowFunctionExpression() && !this.isFunctionExpression() && !this.isFunctionDeclaration()) {
91 throw this.buildCodeFrameError("Can only unwrap the environment of a function.");
92 }
93
94 hoistFunctionEnvironment(this);
95}
96
97function arrowFunctionToExpression({
98 allowInsertArrow = true,
99 specCompliant = false
100} = {}) {
101 if (!this.isArrowFunctionExpression()) {
102 throw this.buildCodeFrameError("Cannot convert non-arrow function to a function expression.");
103 }
104
105 const thisBinding = hoistFunctionEnvironment(this, specCompliant, allowInsertArrow);
106 this.ensureBlock();
107 this.node.type = "FunctionExpression";
108
109 if (specCompliant) {
110 const checkBinding = thisBinding ? null : this.parentPath.scope.generateUidIdentifier("arrowCheckId");
111
112 if (checkBinding) {
113 this.parentPath.scope.push({
114 id: checkBinding,
115 init: t.objectExpression([])
116 });
117 }
118
119 this.get("body").unshiftContainer("body", t.expressionStatement(t.callExpression(this.hub.addHelper("newArrowCheck"), [t.thisExpression(), checkBinding ? t.identifier(checkBinding.name) : t.identifier(thisBinding)])));
120 this.replaceWith(t.callExpression(t.memberExpression((0, _helperFunctionName.default)(this, true) || this.node, t.identifier("bind")), [checkBinding ? t.identifier(checkBinding.name) : t.thisExpression()]));
121 }
122}
123
124function hoistFunctionEnvironment(fnPath, specCompliant = false, allowInsertArrow = true) {
125 const thisEnvFn = fnPath.findParent(p => {
126 return p.isFunction() && !p.isArrowFunctionExpression() || p.isProgram() || p.isClassProperty({
127 static: false
128 });
129 });
130 const inConstructor = thisEnvFn && thisEnvFn.node.kind === "constructor";
131
132 if (thisEnvFn.isClassProperty()) {
133 throw fnPath.buildCodeFrameError("Unable to transform arrow inside class property");
134 }
135
136 const {
137 thisPaths,
138 argumentsPaths,
139 newTargetPaths,
140 superProps,
141 superCalls
142 } = getScopeInformation(fnPath);
143
144 if (inConstructor && superCalls.length > 0) {
145 if (!allowInsertArrow) {
146 throw superCalls[0].buildCodeFrameError("Unable to handle nested super() usage in arrow");
147 }
148
149 const allSuperCalls = [];
150 thisEnvFn.traverse({
151 Function(child) {
152 if (child.isArrowFunctionExpression()) return;
153 child.skip();
154 },
155
156 ClassProperty(child) {
157 child.skip();
158 },
159
160 CallExpression(child) {
161 if (!child.get("callee").isSuper()) return;
162 allSuperCalls.push(child);
163 }
164
165 });
166 const superBinding = getSuperBinding(thisEnvFn);
167 allSuperCalls.forEach(superCall => {
168 const callee = t.identifier(superBinding);
169 callee.loc = superCall.node.callee.loc;
170 superCall.get("callee").replaceWith(callee);
171 });
172 }
173
174 if (argumentsPaths.length > 0) {
175 const argumentsBinding = getBinding(thisEnvFn, "arguments", () => t.identifier("arguments"));
176 argumentsPaths.forEach(argumentsChild => {
177 const argsRef = t.identifier(argumentsBinding);
178 argsRef.loc = argumentsChild.node.loc;
179 argumentsChild.replaceWith(argsRef);
180 });
181 }
182
183 if (newTargetPaths.length > 0) {
184 const newTargetBinding = getBinding(thisEnvFn, "newtarget", () => t.metaProperty(t.identifier("new"), t.identifier("target")));
185 newTargetPaths.forEach(targetChild => {
186 const targetRef = t.identifier(newTargetBinding);
187 targetRef.loc = targetChild.node.loc;
188 targetChild.replaceWith(targetRef);
189 });
190 }
191
192 if (superProps.length > 0) {
193 if (!allowInsertArrow) {
194 throw superProps[0].buildCodeFrameError("Unable to handle nested super.prop usage");
195 }
196
197 const flatSuperProps = superProps.reduce((acc, superProp) => acc.concat(standardizeSuperProperty(superProp)), []);
198 flatSuperProps.forEach(superProp => {
199 const key = superProp.node.computed ? "" : superProp.get("property").node.name;
200 const isAssignment = superProp.parentPath.isAssignmentExpression({
201 left: superProp.node
202 });
203 const isCall = superProp.parentPath.isCallExpression({
204 callee: superProp.node
205 });
206 const superBinding = getSuperPropBinding(thisEnvFn, isAssignment, key);
207 const args = [];
208
209 if (superProp.node.computed) {
210 args.push(superProp.get("property").node);
211 }
212
213 if (isAssignment) {
214 const value = superProp.parentPath.node.right;
215 args.push(value);
216 }
217
218 const call = t.callExpression(t.identifier(superBinding), args);
219
220 if (isCall) {
221 superProp.parentPath.unshiftContainer("arguments", t.thisExpression());
222 superProp.replaceWith(t.memberExpression(call, t.identifier("call")));
223 thisPaths.push(superProp.parentPath.get("arguments.0"));
224 } else if (isAssignment) {
225 superProp.parentPath.replaceWith(call);
226 } else {
227 superProp.replaceWith(call);
228 }
229 });
230 }
231
232 let thisBinding;
233
234 if (thisPaths.length > 0 || specCompliant) {
235 thisBinding = getThisBinding(thisEnvFn, inConstructor);
236
237 if (!specCompliant || inConstructor && hasSuperClass(thisEnvFn)) {
238 thisPaths.forEach(thisChild => {
239 const thisRef = thisChild.isJSX() ? t.jsxIdentifier(thisBinding) : t.identifier(thisBinding);
240 thisRef.loc = thisChild.node.loc;
241 thisChild.replaceWith(thisRef);
242 });
243 if (specCompliant) thisBinding = null;
244 }
245 }
246
247 return thisBinding;
248}
249
250function standardizeSuperProperty(superProp) {
251 if (superProp.parentPath.isAssignmentExpression() && superProp.parentPath.node.operator !== "=") {
252 const assignmentPath = superProp.parentPath;
253 const op = assignmentPath.node.operator.slice(0, -1);
254 const value = assignmentPath.node.right;
255 assignmentPath.node.operator = "=";
256
257 if (superProp.node.computed) {
258 const tmp = superProp.scope.generateDeclaredUidIdentifier("tmp");
259 assignmentPath.get("left").replaceWith(t.memberExpression(superProp.node.object, t.assignmentExpression("=", tmp, superProp.node.property), true));
260 assignmentPath.get("right").replaceWith(t.binaryExpression(op, t.memberExpression(superProp.node.object, t.identifier(tmp.name), true), value));
261 } else {
262 assignmentPath.get("left").replaceWith(t.memberExpression(superProp.node.object, superProp.node.property));
263 assignmentPath.get("right").replaceWith(t.binaryExpression(op, t.memberExpression(superProp.node.object, t.identifier(superProp.node.property.name)), value));
264 }
265
266 return [assignmentPath.get("left"), assignmentPath.get("right").get("left")];
267 } else if (superProp.parentPath.isUpdateExpression()) {
268 const updateExpr = superProp.parentPath;
269 const tmp = superProp.scope.generateDeclaredUidIdentifier("tmp");
270 const computedKey = superProp.node.computed ? superProp.scope.generateDeclaredUidIdentifier("prop") : null;
271 const parts = [t.assignmentExpression("=", tmp, t.memberExpression(superProp.node.object, computedKey ? t.assignmentExpression("=", computedKey, superProp.node.property) : superProp.node.property, superProp.node.computed)), t.assignmentExpression("=", t.memberExpression(superProp.node.object, computedKey ? t.identifier(computedKey.name) : superProp.node.property, superProp.node.computed), t.binaryExpression("+", t.identifier(tmp.name), t.numericLiteral(1)))];
272
273 if (!superProp.parentPath.node.prefix) {
274 parts.push(t.identifier(tmp.name));
275 }
276
277 updateExpr.replaceWith(t.sequenceExpression(parts));
278 const left = updateExpr.get("expressions.0.right");
279 const right = updateExpr.get("expressions.1.left");
280 return [left, right];
281 }
282
283 return [superProp];
284}
285
286function hasSuperClass(thisEnvFn) {
287 return thisEnvFn.isClassMethod() && !!thisEnvFn.parentPath.parentPath.node.superClass;
288}
289
290function getThisBinding(thisEnvFn, inConstructor) {
291 return getBinding(thisEnvFn, "this", thisBinding => {
292 if (!inConstructor || !hasSuperClass(thisEnvFn)) return t.thisExpression();
293 const supers = new WeakSet();
294 thisEnvFn.traverse({
295 Function(child) {
296 if (child.isArrowFunctionExpression()) return;
297 child.skip();
298 },
299
300 ClassProperty(child) {
301 child.skip();
302 },
303
304 CallExpression(child) {
305 if (!child.get("callee").isSuper()) return;
306 if (supers.has(child.node)) return;
307 supers.add(child.node);
308 child.replaceWithMultiple([child.node, t.assignmentExpression("=", t.identifier(thisBinding), t.identifier("this"))]);
309 }
310
311 });
312 });
313}
314
315function getSuperBinding(thisEnvFn) {
316 return getBinding(thisEnvFn, "supercall", () => {
317 const argsBinding = thisEnvFn.scope.generateUidIdentifier("args");
318 return t.arrowFunctionExpression([t.restElement(argsBinding)], t.callExpression(t.super(), [t.spreadElement(t.identifier(argsBinding.name))]));
319 });
320}
321
322function getSuperPropBinding(thisEnvFn, isAssignment, propName) {
323 const op = isAssignment ? "set" : "get";
324 return getBinding(thisEnvFn, `superprop_${op}:${propName || ""}`, () => {
325 const argsList = [];
326 let fnBody;
327
328 if (propName) {
329 fnBody = t.memberExpression(t.super(), t.identifier(propName));
330 } else {
331 const method = thisEnvFn.scope.generateUidIdentifier("prop");
332 argsList.unshift(method);
333 fnBody = t.memberExpression(t.super(), t.identifier(method.name), true);
334 }
335
336 if (isAssignment) {
337 const valueIdent = thisEnvFn.scope.generateUidIdentifier("value");
338 argsList.push(valueIdent);
339 fnBody = t.assignmentExpression("=", fnBody, t.identifier(valueIdent.name));
340 }
341
342 return t.arrowFunctionExpression(argsList, fnBody);
343 });
344}
345
346function getBinding(thisEnvFn, key, init) {
347 const cacheKey = "binding:" + key;
348 let data = thisEnvFn.getData(cacheKey);
349
350 if (!data) {
351 const id = thisEnvFn.scope.generateUidIdentifier(key);
352 data = id.name;
353 thisEnvFn.setData(cacheKey, data);
354 thisEnvFn.scope.push({
355 id: id,
356 init: init(data)
357 });
358 }
359
360 return data;
361}
362
363function getScopeInformation(fnPath) {
364 const thisPaths = [];
365 const argumentsPaths = [];
366 const newTargetPaths = [];
367 const superProps = [];
368 const superCalls = [];
369 fnPath.traverse({
370 ClassProperty(child) {
371 child.skip();
372 },
373
374 Function(child) {
375 if (child.isArrowFunctionExpression()) return;
376 child.skip();
377 },
378
379 ThisExpression(child) {
380 thisPaths.push(child);
381 },
382
383 JSXIdentifier(child) {
384 if (child.node.name !== "this") return;
385
386 if (!child.parentPath.isJSXMemberExpression({
387 object: child.node
388 }) && !child.parentPath.isJSXOpeningElement({
389 name: child.node
390 })) {
391 return;
392 }
393
394 thisPaths.push(child);
395 },
396
397 CallExpression(child) {
398 if (child.get("callee").isSuper()) superCalls.push(child);
399 },
400
401 MemberExpression(child) {
402 if (child.get("object").isSuper()) superProps.push(child);
403 },
404
405 ReferencedIdentifier(child) {
406 if (child.node.name !== "arguments") return;
407 argumentsPaths.push(child);
408 },
409
410 MetaProperty(child) {
411 if (!child.get("meta").isIdentifier({
412 name: "new"
413 })) return;
414 if (!child.get("property").isIdentifier({
415 name: "target"
416 })) return;
417 newTargetPaths.push(child);
418 }
419
420 });
421 return {
422 thisPaths,
423 argumentsPaths,
424 newTargetPaths,
425 superProps,
426 superCalls
427 };
428}
\No newline at end of file