Nicolò Ribaudo 05b22d2597
Update @babel/helper-wrap-function templates (#6984)
This commit introduces 4 changes:

1) Function declarations are wrapped using function declarations.
   This has two advantages:
    - We can rely on native hoisting, instead of using _blockHoist
    - The function isn't wrapped until it is called. This avoids
      problems where `regeneratorRuntime.wrap` was called before
      that `babel-polyfill` was imported.

   Example:
     function fn() {}
     // becomes
     function fn() { return _fn.apply(this, arguments); }
     function _fn() {
       _fn = _wrapper(/* Original function ... */);
       return _fn.apply(this, arguments);
     }

2) Use a single template for both named and anonymous function
   expressions. They already had the same behavior, but the one
   used for named functions was a bit longer.

3) Use normal functions instead of arrow functions to wrap
   function expressions.

4) Generate a name based on the original one for wrapped
   functions (e.g. `foo` becomes `_foo` instead of `_ref`).
2017-12-13 16:21:58 +01:00

88 lines
2.1 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 * as t from "@babel/types";
import rewriteForAwait from "./for-await";
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(
t.yieldExpression(
wrapAwait
? t.callExpression(wrapAwait, [argument.node])
: argument.node,
),
);
},
ForOfStatement(path, { file, wrapAwait }) {
const { node } = path;
if (!node.await) return;
const build = rewriteForAwait(path, {
getAsyncIterator: file.addHelper("asyncIterator"),
wrapAwait,
});
const { declar, loop } = build;
const block = loop.body;
// ensure that it's a block so we can take all its statements
path.ensureBlock();
// add the value declaration to the new loop body
if (declar) {
block.body.push(declar);
}
// push the rest of the original loop body onto our new body
block.body = block.body.concat(node.body.body);
t.inherits(loop, node);
t.inherits(loop.body, node.body);
if (build.replaceParent) {
path.parentPath.replaceWithMultiple(build.node);
} else {
path.replaceWithMultiple(build.node);
}
},
};
export default function(path: NodePath, file: Object, helpers: Object) {
path.traverse(awaitVisitor, {
file,
wrapAwait: helpers.wrapAwait,
});
const isIIFE = path.parentPath.isCallExpression({ callee: path.node });
path.node.async = false;
path.node.generator = true;
wrapFunction(path, helpers.wrapAsync);
const isProperty =
path.isObjectMethod() ||
path.isClassMethod() ||
path.parentPath.isObjectProperty() ||
path.parentPath.isClassProperty();
if (!isProperty && !isIIFE && path.isExpression()) {
annotateAsPure(path);
}
}