133 lines
3.1 KiB
JavaScript
133 lines
3.1 KiB
JavaScript
/* @noflow */
|
|
|
|
import type { NodePath } from "babel-traverse";
|
|
import nameFunction from "babel-helper-function-name";
|
|
import template from "babel-template";
|
|
import * as t from "babel-types";
|
|
|
|
let buildWrapper = template(`
|
|
(function () {
|
|
var ref = FUNCTION;
|
|
return function (PARAMS) {
|
|
return ref.apply(this, arguments);
|
|
};
|
|
})
|
|
`);
|
|
|
|
let arrowBuildWrapper = template(`
|
|
(() => {
|
|
var ref = FUNCTION, _this = this;
|
|
return function(PARAMS) {
|
|
return ref.apply(_this, arguments);
|
|
};
|
|
})
|
|
`);
|
|
|
|
let awaitVisitor = {
|
|
ArrowFunctionExpression(path) {
|
|
if (!path.node.async) {
|
|
path.arrowFunctionToShadowed();
|
|
}
|
|
},
|
|
|
|
AwaitExpression({ node }) {
|
|
node.type = "YieldExpression";
|
|
}
|
|
};
|
|
|
|
function classOrObjectMethod(path: NodePath, callId: Object) {
|
|
let node = path.node;
|
|
let body = node.body;
|
|
|
|
node.async = false;
|
|
|
|
let container = t.functionExpression(null, [], t.blockStatement(body.body), true);
|
|
container.shadow = true;
|
|
body.body = [
|
|
t.returnStatement(t.callExpression(
|
|
t.callExpression(callId, [container]),
|
|
[]
|
|
))
|
|
];
|
|
}
|
|
|
|
function plainFunction(path: NodePath, callId: Object) {
|
|
let node = path.node;
|
|
let wrapper = buildWrapper;
|
|
|
|
if (path.isArrowFunctionExpression()) {
|
|
path.arrowFunctionToShadowed();
|
|
wrapper = arrowBuildWrapper;
|
|
}
|
|
|
|
node.async = false;
|
|
node.generator = true;
|
|
// Either the wrapped generator is invoked with `.apply(this, arguments)` or it has no params,
|
|
// so it should capture `arguments`
|
|
if (node.shadow) {
|
|
// node.shadow may be `true` or an object
|
|
node.shadow = Object.assign({}, node.shadow, { arguments: false });
|
|
}
|
|
|
|
let asyncFnId = node.id;
|
|
node.id = null;
|
|
|
|
let isDeclaration = path.isFunctionDeclaration();
|
|
|
|
if (isDeclaration) {
|
|
node.type = "FunctionExpression";
|
|
}
|
|
|
|
let built = t.callExpression(callId, [node]);
|
|
let container = wrapper({
|
|
FUNCTION: built,
|
|
PARAMS: node.params.map(() => path.scope.generateUidIdentifier("x"))
|
|
}).expression;
|
|
|
|
let retFunction = container.body.body[1].argument;
|
|
|
|
if (isDeclaration) {
|
|
let declar = t.variableDeclaration("let", [
|
|
t.variableDeclarator(
|
|
t.identifier(asyncFnId.name),
|
|
t.callExpression(container, [])
|
|
)
|
|
]);
|
|
declar._blockHoist = true;
|
|
|
|
retFunction.id = asyncFnId;
|
|
path.replaceWith(declar);
|
|
} else {
|
|
if (asyncFnId && asyncFnId.name) {
|
|
retFunction.id = asyncFnId;
|
|
} else {
|
|
nameFunction({
|
|
node: retFunction,
|
|
parent: path.parent,
|
|
scope: path.scope
|
|
});
|
|
}
|
|
|
|
if (retFunction.id || node.params.length) {
|
|
// we have an inferred function id or params so we need this wrapper
|
|
path.replaceWith(t.callExpression(container, []));
|
|
} else {
|
|
// we can omit this wrapper as the conditions it protects for do not apply
|
|
path.replaceWith(built);
|
|
}
|
|
}
|
|
}
|
|
|
|
export default function (path: NodePath, callId: Object) {
|
|
let node = path.node;
|
|
if (node.generator) return;
|
|
|
|
path.traverse(awaitVisitor);
|
|
|
|
if (path.isClassMethod() || path.isObjectMethod()) {
|
|
return classOrObjectMethod(path, callId);
|
|
} else {
|
|
return plainFunction(path, callId);
|
|
}
|
|
}
|