Nicolò Ribaudo 6ef7b51a11 Implement assumptions defined in the babel/rfcs#5 RFC
- `mutableTemplateObject` and `ignoreToPrimitiveHint` (#12408)
- `setClassMethods` (#12407)
- `setComputedProperties` (#12490)
- `ignoreFunctionLength` (#12491)
- `noDocumentAll` (#12481)
- `iterableIsArray` and `arrayLikeIsIterable` (#12489)
- `pureGetters` (#12504)
- `skipForOfIteratorClosing` (#12496)
- `objectRestNoSymbols`, `setSpreadProperties` and `pureGetters` (#12505)
- `noNewArrows` (#12613, #12793)
- `setPublicClassFields` and `privateFieldsAsProperties` (#12497)
- `constantReexports` and `enumerableModuleMeta` (#12618)
- `constantSuper`, `superIsCallableConstructor` and `noClassCalls` (#12726)

Co-authored-by: Justin Ridgewell <justin@ridgewell.name>
Co-authored-by: Huáng Jùnliàng <JLHwung@users.noreply.github.com>
2021-02-21 17:09:45 +01:00

138 lines
3.4 KiB
JavaScript

import type { NodePath } from "@babel/traverse";
import nameFunction from "@babel/helper-function-name";
import template from "@babel/template";
import * as t from "@babel/types";
const buildAnonymousExpressionWrapper = template.expression(`
(function () {
var REF = FUNCTION;
return function NAME(PARAMS) {
return REF.apply(this, arguments);
};
})()
`);
const buildNamedExpressionWrapper = template.expression(`
(function () {
var REF = FUNCTION;
function NAME(PARAMS) {
return REF.apply(this, arguments);
}
return NAME;
})()
`);
const buildDeclarationWrapper = template(`
function NAME(PARAMS) { return REF.apply(this, arguments); }
function REF() {
REF = FUNCTION;
return REF.apply(this, arguments);
}
`);
function classOrObjectMethod(path: NodePath, callId: Object) {
const node = path.node;
const body = node.body;
const container = t.functionExpression(
null,
[],
t.blockStatement(body.body),
true,
);
body.body = [
t.returnStatement(
t.callExpression(t.callExpression(callId, [container]), []),
),
];
// Regardless of whether or not the wrapped function is a an async method
// or generator the outer function should not be
node.async = false;
node.generator = false;
// Unwrap the wrapper IIFE's environment so super and this and such still work.
path
.get("body.body.0.argument.callee.arguments.0")
.unwrapFunctionEnvironment();
}
function plainFunction(path: NodePath, callId: Object, noNewArrows: boolean) {
const node = path.node;
const isDeclaration = path.isFunctionDeclaration();
const functionId = node.id;
const wrapper = isDeclaration
? buildDeclarationWrapper
: functionId
? buildNamedExpressionWrapper
: buildAnonymousExpressionWrapper;
if (path.isArrowFunctionExpression()) {
path.arrowFunctionToExpression({ noNewArrows });
}
node.id = null;
if (isDeclaration) {
node.type = "FunctionExpression";
}
const built = t.callExpression(callId, [node]);
const container = wrapper({
NAME: functionId || null,
REF: path.scope.generateUidIdentifier(functionId ? functionId.name : "ref"),
FUNCTION: built,
PARAMS: node.params.reduce(
(acc, param) => {
acc.done =
acc.done || t.isAssignmentPattern(param) || t.isRestElement(param);
if (!acc.done) {
acc.params.push(path.scope.generateUidIdentifier("x"));
}
return acc;
},
{
params: [],
done: false,
},
).params,
});
if (isDeclaration) {
path.replaceWith(container[0]);
path.insertAfter(container[1]);
} else {
const retFunction = container.callee.body.body[1].argument;
if (!functionId) {
nameFunction({
node: retFunction,
parent: path.parent,
scope: path.scope,
});
}
if (!retFunction || retFunction.id || node.params.length) {
// we have an inferred function id or params so we need this wrapper
path.replaceWith(container);
} else {
// we can omit this wrapper as the conditions it protects for do not apply
path.replaceWith(built);
}
}
}
export default function wrapFunction(
path: NodePath,
callId: Object,
// TODO(Babel 8): Consider defaulting to false for spec compliancy
noNewArrows: boolean = true,
) {
if (path.isMethod()) {
classOrObjectMethod(path, callId);
} else {
plainFunction(path, callId, noNewArrows);
}
}