diff --git a/packages/babel-plugin-jest-hoist/src/__tests__/__snapshots__/hoistPlugin.test.ts.snap b/packages/babel-plugin-jest-hoist/src/__tests__/__snapshots__/hoistPlugin.test.ts.snap
index 3255331311b5..09486e91ae92 100644
--- a/packages/babel-plugin-jest-hoist/src/__tests__/__snapshots__/hoistPlugin.test.ts.snap
+++ b/packages/babel-plugin-jest-hoist/src/__tests__/__snapshots__/hoistPlugin.test.ts.snap
@@ -6,6 +6,8 @@ jest.mock('./App', () => () =>
Hello world
);
↓ ↓ ↓ ↓ ↓ ↓
+var _jsxFileName = "/root/project/src/file.js";
+
_getJestObj().mock("./App", () => () =>
/*#__PURE__*/ _jsxDEV(
"div",
@@ -15,7 +17,7 @@ _getJestObj().mock("./App", () => () =>
void 0,
false,
{
- fileName: _hoistedMockFactoryVariable(),
+ fileName: _jsxFileName,
lineNumber: 1,
columnNumber: 32
},
@@ -24,7 +26,6 @@ _getJestObj().mock("./App", () => () =>
);
import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";
-var _jsxFileName = "/root/project/src/file.js";
function _getJestObj() {
const { jest } = require("@jest/globals");
@@ -34,10 +35,6 @@ function _getJestObj() {
return jest;
}
-function _hoistedMockFactoryVariable() {
- return "/root/project/src/file.js";
-}
-
`;
diff --git a/packages/babel-plugin-jest-hoist/src/index.ts b/packages/babel-plugin-jest-hoist/src/index.ts
index 7ecafb8847da..e6d37508871f 100644
--- a/packages/babel-plugin-jest-hoist/src/index.ts
+++ b/packages/babel-plugin-jest-hoist/src/index.ts
@@ -16,15 +16,20 @@ import {
Identifier,
Node,
Program,
+ VariableDeclaration,
+ VariableDeclarator,
callExpression,
emptyStatement,
isIdentifier,
+ variableDeclaration,
} from '@babel/types';
const JEST_GLOBAL_NAME = 'jest';
const JEST_GLOBALS_MODULE_NAME = '@jest/globals';
const JEST_GLOBALS_MODULE_JEST_EXPORT_NAME = 'jest';
+const hoistedVariables = new WeakSet();
+
// We allow `jest`, `expect`, `require`, all default Node.js globals and all
// ES2015 built-ins to be used inside of a `jest.mock` factory.
// We also allow variables prefixed with `mock` as an escape-hatch.
@@ -146,20 +151,7 @@ FUNCTIONS.mock = args => {
const initNode = binding.path.node.init;
if (initNode && binding.constant && scope.isPure(initNode, true)) {
- const identifier = scope.generateUidIdentifier(
- 'hoistedMockFactoryVariable',
- );
-
- binding.path.parentPath.insertAfter(
- createHoistedVariable({
- HOISTED_NAME: identifier,
- HOISTED_VALUE: binding.path.node.init,
- }),
- );
-
- // replace reference with a call to the new function
- id.replaceWith(callExpression(identifier, []));
-
+ hoistedVariables.add(binding.path.node);
isAllowedIdentifier = true;
}
}
@@ -202,12 +194,6 @@ function GETTER_NAME() {
}
`;
-const createHoistedVariable = statement`
-function HOISTED_NAME() {
- return HOISTED_VALUE;
-}
-`;
-
const isJestObject = (expression: NodePath): boolean => {
// global
if (
@@ -317,6 +303,7 @@ export default (): PluginObj<{
// in `post` to make sure we come after an import transform and can unshift above the `require`s
post({path: program}) {
const self = this;
+
visitBlock(program);
program.traverse({
BlockStatement: visitBlock,
@@ -324,17 +311,19 @@ export default (): PluginObj<{
function visitBlock(block: NodePath | NodePath) {
// use a temporary empty statement instead of the real first statement, which may itself be hoisted
- const [firstNonHoistedStatementOfBlock] = block.unshiftContainer(
- 'body',
+ const [varsHoistPoint, callsHoistPoint] = block.unshiftContainer('body', [
emptyStatement(),
- );
+ emptyStatement(),
+ ]);
block.traverse({
CallExpression: visitCallExpr,
+ VariableDeclarator: visitVariableDeclarator,
// do not traverse into nested blocks, or we'll hoist calls in there out to this block
// @ts-expect-error blacklist is not known
blacklist: ['BlockStatement'],
});
- firstNonHoistedStatementOfBlock.remove();
+ callsHoistPoint.remove();
+ varsHoistPoint.remove();
function visitCallExpr(callExpr: NodePath) {
const {
@@ -351,11 +340,25 @@ export default (): PluginObj<{
const mockStmtParent = mockStmt.parentPath;
if (mockStmtParent.isBlock()) {
mockStmt.remove();
- firstNonHoistedStatementOfBlock.insertBefore(mockStmtNode);
+ callsHoistPoint.insertBefore(mockStmtNode);
}
}
}
}
+
+ function visitVariableDeclarator(varDecl: NodePath) {
+ if (hoistedVariables.has(varDecl.node)) {
+ const {kind, declarations} = varDecl.parent as VariableDeclaration;
+ if (declarations.length === 1) {
+ varDecl.parentPath.remove();
+ } else {
+ varDecl.remove();
+ }
+ varsHoistPoint.insertBefore(
+ variableDeclaration(kind, [varDecl.node]),
+ );
+ }
+ }
}
},
});