* migrate to named babel types imports * perf: transform babel types import to destructuring * fix merge errors * apply plugin to itself
93 lines
2.3 KiB
JavaScript
93 lines
2.3 KiB
JavaScript
/* @noflow */
|
|
|
|
import type { NodePath } from "@babel/traverse";
|
|
import wrapFunction from "@babel/helper-wrap-function";
|
|
import annotateAsPure from "@babel/helper-annotate-as-pure";
|
|
import {
|
|
callExpression,
|
|
cloneNode,
|
|
isIdentifier,
|
|
isThisExpression,
|
|
yieldExpression,
|
|
} from "@babel/types";
|
|
|
|
const awaitVisitor = {
|
|
Function(path) {
|
|
path.skip();
|
|
},
|
|
|
|
AwaitExpression(path, { wrapAwait }) {
|
|
const argument = path.get("argument");
|
|
|
|
if (path.parentPath.isYieldExpression()) {
|
|
path.replaceWith(argument.node);
|
|
return;
|
|
}
|
|
|
|
path.replaceWith(
|
|
yieldExpression(
|
|
wrapAwait
|
|
? callExpression(cloneNode(wrapAwait), [argument.node])
|
|
: argument.node,
|
|
),
|
|
);
|
|
},
|
|
};
|
|
|
|
export default function (
|
|
path: NodePath,
|
|
helpers: { wrapAsync: Object, wrapAwait: Object },
|
|
noNewArrows?: boolean,
|
|
) {
|
|
path.traverse(awaitVisitor, {
|
|
wrapAwait: helpers.wrapAwait,
|
|
});
|
|
|
|
const isIIFE = checkIsIIFE(path);
|
|
|
|
path.node.async = false;
|
|
path.node.generator = true;
|
|
|
|
wrapFunction(path, cloneNode(helpers.wrapAsync), noNewArrows);
|
|
|
|
const isProperty =
|
|
path.isObjectMethod() ||
|
|
path.isClassMethod() ||
|
|
path.parentPath.isObjectProperty() ||
|
|
path.parentPath.isClassProperty();
|
|
|
|
if (!isProperty && !isIIFE && path.isExpression()) {
|
|
annotateAsPure(path);
|
|
}
|
|
|
|
function checkIsIIFE(path: NodePath) {
|
|
if (path.parentPath.isCallExpression({ callee: path.node })) {
|
|
return true;
|
|
}
|
|
|
|
// try to catch calls to Function#bind, as emitted by arrowFunctionToExpression in spec mode
|
|
// this may also catch .bind(this) written by users, but does it matter? 🤔
|
|
const { parentPath } = path;
|
|
if (
|
|
parentPath.isMemberExpression() &&
|
|
isIdentifier(parentPath.node.property, { name: "bind" })
|
|
) {
|
|
const { parentPath: bindCall } = parentPath;
|
|
|
|
// (function () { ... }).bind(this)()
|
|
|
|
return (
|
|
// first, check if the .bind is actually being called
|
|
bindCall.isCallExpression() &&
|
|
// and whether its sole argument is 'this'
|
|
bindCall.node.arguments.length === 1 &&
|
|
isThisExpression(bindCall.node.arguments[0]) &&
|
|
// and whether the result of the .bind(this) is being called
|
|
bindCall.parentPath.isCallExpression({ callee: bindCall.node })
|
|
);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|