Skip to content

Instantly share code, notes, and snippets.

@motiz88
Created July 18, 2017 09:35
Show Gist options
  • Save motiz88/a3513e03ca911391f8a578366bfa0bed to your computer and use it in GitHub Desktop.
Save motiz88/a3513e03ca911391f8a578366bfa0bed to your computer and use it in GitHub Desktop.
// Work in progress
// Babel plugin to convert Node-style callback code to async/await
export default function(babel) {
const { types: t } = babel;
const asyncControlFlowVisitor = {
CallExpression(path) {
const callee = path.get("callee");
if (
callee.isIdentifier() &&
callee.scope.getBinding(callee.node.name) === this.callbackParamBinding
) {
if (path.node.arguments.length > 2) {
return;
}
if (!path.parentPath.isExpressionStatement()) {
return;
}
if (
path.getFunctionParent().node !==
this.callbackParamBinding.path.getFunctionParent().node
) {
return;
}
const errArg = path.get("arguments")[0];
const valueArg = path.get("arguments")[1];
const errEvaluatedBool = errArg && errArg.evaluateTruthy();
if (errEvaluatedBool === true) {
path.parentPath.replaceWith(t.throwStatement(errArg.node));
} else if (errEvaluatedBool === false || !errArg) {
path.parentPath.replaceWith(
t.returnStatement(valueArg && valueArg.node)
);
} else {
const nodes = [];
const { scope } = path;
let errArgNode = errArg.node,
valueArgNode = valueArg && valueArg.node;
if (!errArg.scope.isPure(errArgNode)) {
const temp = scope.generateUidIdentifierBasedOnNode(errArgNode);
nodes.push(
t.variableDeclaration("const", [
t.variableDeclarator(temp, errArgNode)
])
);
errArgNode = temp;
scope.crawl();
}
if (valueArg && !valueArg.scope.isPure(valueArgNode)) {
const temp = scope.generateUidIdentifierBasedOnNode(valueArgNode);
nodes.push(
t.variableDeclaration("const", [
t.variableDeclarator(temp, valueArgNode)
])
);
valueArgNode = temp;
scope.crawl();
}
nodes.push(
t.ifStatement(
errArgNode,
t.throwStatement(errArgNode),
t.returnStatement(valueArgNode)
)
);
path.parentPath.replaceWithMultiple(nodes);
}
}
}
};
return {
name: "ast-transform", // not required
visitor: {
Function(path) {
const params = path.get("params");
const lastParam = params[params.length - 1];
if (!lastParam) {
return;
}
if (!isCallbackParam(lastParam)) {
return;
}
console.log(lastParam);
path.traverse(asyncControlFlowVisitor, {
callbackParamBinding: lastParam.scope.getBinding(lastParam.node.name)
});
path.node.async = true;
}
}
};
}
function isCallbackParam(path) {
return path.isIdentifier() &&
["callback", "cb", "done"].includes(path.node.name);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment