babel-plugin-jest-hoist
Advanced tools
Comparing version 29.6.3 to 30.0.0-alpha.0
@@ -1,9 +0,23 @@ | ||
'use strict'; | ||
/*! | ||
* /** | ||
* * Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* * | ||
* * This source code is licensed under the MIT license found in the | ||
* * LICENSE file in the root directory of this source tree. | ||
* * / | ||
*/ | ||
/******/ (() => { // webpackBootstrap | ||
/******/ "use strict"; | ||
var __webpack_exports__ = {}; | ||
// This entry need to be wrapped in an IIFE because it uses a non-standard name for the exports (exports). | ||
(() => { | ||
var exports = __webpack_exports__; | ||
Object.defineProperty(exports, '__esModule', { | ||
Object.defineProperty(exports, "__esModule", ({ | ||
value: true | ||
}); | ||
exports.default = jestHoist; | ||
})); | ||
exports["default"] = jestHoist; | ||
function _template() { | ||
const data = require('@babel/template'); | ||
const data = require("@babel/template"); | ||
_template = function () { | ||
@@ -15,3 +29,3 @@ return data; | ||
function _types() { | ||
const data = require('@babel/types'); | ||
const data = require("@babel/types"); | ||
_types = function () { | ||
@@ -40,73 +54,10 @@ return data; | ||
// We also allow variables prefixed with `mock` as an escape-hatch. | ||
const ALLOWED_IDENTIFIERS = new Set( | ||
[ | ||
'Array', | ||
'ArrayBuffer', | ||
'Boolean', | ||
'BigInt', | ||
'DataView', | ||
'Date', | ||
'Error', | ||
'EvalError', | ||
'Float32Array', | ||
'Float64Array', | ||
'Function', | ||
'Generator', | ||
'GeneratorFunction', | ||
'Infinity', | ||
'Int16Array', | ||
'Int32Array', | ||
'Int8Array', | ||
'InternalError', | ||
'Intl', | ||
'JSON', | ||
'Map', | ||
'Math', | ||
'NaN', | ||
'Number', | ||
'Object', | ||
'Promise', | ||
'Proxy', | ||
'RangeError', | ||
'ReferenceError', | ||
'Reflect', | ||
'RegExp', | ||
'Set', | ||
'String', | ||
'Symbol', | ||
'SyntaxError', | ||
'TypeError', | ||
'URIError', | ||
'Uint16Array', | ||
'Uint32Array', | ||
'Uint8Array', | ||
'Uint8ClampedArray', | ||
'WeakMap', | ||
'WeakSet', | ||
'arguments', | ||
'console', | ||
'expect', | ||
'isNaN', | ||
'jest', | ||
'parseFloat', | ||
'parseInt', | ||
'exports', | ||
'require', | ||
'module', | ||
'__filename', | ||
'__dirname', | ||
'undefined', | ||
...Object.getOwnPropertyNames(globalThis) | ||
].sort() | ||
); | ||
const ALLOWED_IDENTIFIERS = new Set(['Array', 'ArrayBuffer', 'Boolean', 'BigInt', 'DataView', 'Date', 'Error', 'EvalError', 'Float32Array', 'Float64Array', 'Function', 'Generator', 'GeneratorFunction', 'Infinity', 'Int16Array', 'Int32Array', 'Int8Array', 'InternalError', 'Intl', 'JSON', 'Map', 'Math', 'NaN', 'Number', 'Object', 'Promise', 'Proxy', 'RangeError', 'ReferenceError', 'Reflect', 'RegExp', 'Set', 'String', 'Symbol', 'SyntaxError', 'TypeError', 'URIError', 'Uint16Array', 'Uint32Array', 'Uint8Array', 'Uint8ClampedArray', 'WeakMap', 'WeakSet', 'arguments', 'console', 'expect', 'isNaN', 'jest', 'parseFloat', 'parseInt', 'exports', 'require', 'module', '__filename', '__dirname', 'undefined', ...Object.getOwnPropertyNames(globalThis)].sort()); | ||
const IDVisitor = { | ||
ReferencedIdentifier(path, {ids}) { | ||
ReferencedIdentifier(path, { | ||
ids | ||
}) { | ||
ids.add(path); | ||
}, | ||
blacklist: [ | ||
'TypeAnnotation', | ||
'TSTypeAnnotation', | ||
'TSTypeQuery', | ||
'TSTypeReference' | ||
] | ||
denylist: ['TypeAnnotation', 'TSTypeAnnotation', 'TSTypeQuery', 'TSTypeReference'] | ||
}; | ||
@@ -120,10 +71,7 @@ const FUNCTIONS = Object.create(null); | ||
if (!moduleFactory.isFunction()) { | ||
throw moduleFactory.buildCodeFrameError( | ||
'The second argument of `jest.mock` must be an inline function.\n', | ||
TypeError | ||
); | ||
throw moduleFactory.buildCodeFrameError('The second argument of `jest.mock` must be an inline function.\n', TypeError); | ||
} | ||
const ids = new Set(); | ||
const parentScope = moduleFactory.parentPath.scope; | ||
// @ts-expect-error: ReferencedIdentifier and blacklist are not known on visitors | ||
// @ts-expect-error: ReferencedIdentifier and denylist are not known on visitors | ||
moduleFactory.traverse(IDVisitor, { | ||
@@ -133,3 +81,5 @@ ids | ||
for (const id of ids) { | ||
const {name} = id.node; | ||
const { | ||
name | ||
} = id.node; | ||
let found = false; | ||
@@ -145,11 +95,11 @@ let scope = id.scope; | ||
if (!found) { | ||
let isAllowedIdentifier = | ||
(scope.hasGlobal(name) && ALLOWED_IDENTIFIERS.has(name)) || | ||
/^mock/i.test(name) || | ||
// Allow istanbul's coverage variable to pass. | ||
/^(?:__)?cov/.test(name); | ||
let isAllowedIdentifier = scope.hasGlobal(name) && ALLOWED_IDENTIFIERS.has(name) || /^mock/i.test(name) || | ||
// Allow istanbul's coverage variable to pass. | ||
/^(?:__)?cov/.test(name); | ||
if (!isAllowedIdentifier) { | ||
const binding = scope.bindings[name]; | ||
if (binding?.path.isVariableDeclarator()) { | ||
const {node} = binding.path; | ||
const { | ||
node | ||
} = binding.path; | ||
const initNode = node.init; | ||
@@ -163,8 +113,3 @@ if (initNode && binding.constant && scope.isPure(initNode, true)) { | ||
const imported = binding.path.node.imported; | ||
if ( | ||
importDecl.node.source.value === JEST_GLOBALS_MODULE_NAME && | ||
((0, _types().isIdentifier)(imported) | ||
? imported.name | ||
: imported.value) === JEST_GLOBALS_MODULE_JEST_EXPORT_NAME | ||
) { | ||
if (importDecl.node.source.value === JEST_GLOBALS_MODULE_NAME && ((0, _types().isIdentifier)(imported) ? imported.name : imported.value) === JEST_GLOBALS_MODULE_JEST_EXPORT_NAME) { | ||
isAllowedIdentifier = true; | ||
@@ -178,14 +123,3 @@ // Imports are already hoisted, so we don't need to add it | ||
if (!isAllowedIdentifier) { | ||
throw id.buildCodeFrameError( | ||
'The module factory of `jest.mock()` is not allowed to ' + | ||
'reference any out-of-scope variables.\n' + | ||
`Invalid variable access: ${name}\n` + | ||
`Allowed objects: ${Array.from(ALLOWED_IDENTIFIERS).join( | ||
', ' | ||
)}.\n` + | ||
'Note: This is a precaution to guard against uninitialized mock ' + | ||
'variables. If it is ensured that the mock is required lazily, ' + | ||
'variable names prefixed with `mock` (case insensitive) are permitted.\n', | ||
ReferenceError | ||
); | ||
throw id.buildCodeFrameError('The module factory of `jest.mock()` is not allowed to ' + 'reference any out-of-scope variables.\n' + `Invalid variable access: ${name}\n` + `Allowed objects: ${Array.from(ALLOWED_IDENTIFIERS).join(', ')}.\n` + 'Note: This is a precaution to guard against uninitialized mock ' + 'variables. If it is ensured that the mock is required lazily, ' + 'variable names prefixed with `mock` (case insensitive) are permitted.\n', ReferenceError); | ||
} | ||
@@ -200,4 +134,3 @@ } | ||
FUNCTIONS.deepUnmock = args => args.length === 1 && args[0].isStringLiteral(); | ||
FUNCTIONS.disableAutomock = FUNCTIONS.enableAutomock = args => | ||
args.length === 0; | ||
FUNCTIONS.disableAutomock = FUNCTIONS.enableAutomock = args => args.length === 0; | ||
const createJestObjectGetter = (0, _template().statement)` | ||
@@ -212,26 +145,11 @@ function GETTER_NAME() { | ||
// global | ||
if ( | ||
expression.isIdentifier() && | ||
expression.node.name === JEST_GLOBAL_NAME && | ||
!expression.scope.hasBinding(JEST_GLOBAL_NAME) | ||
) { | ||
if (expression.isIdentifier() && expression.node.name === JEST_GLOBAL_NAME && !expression.scope.hasBinding(JEST_GLOBAL_NAME)) { | ||
return true; | ||
} | ||
// import { jest } from '@jest/globals' | ||
if ( | ||
expression.referencesImport( | ||
JEST_GLOBALS_MODULE_NAME, | ||
JEST_GLOBALS_MODULE_JEST_EXPORT_NAME | ||
) | ||
) { | ||
if (expression.referencesImport(JEST_GLOBALS_MODULE_NAME, JEST_GLOBALS_MODULE_JEST_EXPORT_NAME)) { | ||
return true; | ||
} | ||
// import * as JestGlobals from '@jest/globals' | ||
if ( | ||
expression.isMemberExpression() && | ||
!expression.node.computed && | ||
expression.get('object').referencesImport(JEST_GLOBALS_MODULE_NAME, '*') && | ||
expression.node.property.type === 'Identifier' && | ||
expression.node.property.name === JEST_GLOBALS_MODULE_JEST_EXPORT_NAME | ||
) { | ||
if (expression.isMemberExpression() && !expression.node.computed && expression.get('object').referencesImport(JEST_GLOBALS_MODULE_NAME, '*') && expression.node.property.type === 'Identifier' && expression.node.property.name === JEST_GLOBALS_MODULE_JEST_EXPORT_NAME) { | ||
return true; | ||
@@ -253,6 +171,5 @@ } | ||
const propertyName = property.node.name; | ||
const jestObjExpr = isJestObject(object) | ||
? object | ||
: // The Jest object could be returned from another call since the functions are all chainable. | ||
extractJestObjExprIfHoistable(object)?.path; | ||
const jestObjExpr = isJestObject(object) ? object : | ||
// The Jest object could be returned from another call since the functions are all chainable. | ||
extractJestObjExprIfHoistable(object)?.path; | ||
if (!jestObjExpr) { | ||
@@ -267,12 +184,7 @@ return null; | ||
let functionHasHoistableScope = functionIsHoistable; | ||
for ( | ||
let path = expr; | ||
path && !functionHasHoistableScope; | ||
path = path.parentPath | ||
) { | ||
for (let path = expr; path && !functionHasHoistableScope; path = path.parentPath) { | ||
functionHasHoistableScope = hoistedJestExpressions.has( | ||
// @ts-expect-error: it's ok if path.node is not an Expression, .has will | ||
// just return false. | ||
path.node | ||
); | ||
// @ts-expect-error: it's ok if path.node is not an Expression, .has will | ||
// just return false. | ||
path.node); | ||
} | ||
@@ -292,3 +204,5 @@ if (functionHasHoistableScope) { | ||
return { | ||
pre({path: program}) { | ||
pre({ | ||
path: program | ||
}) { | ||
this.declareJestObjGetterIdentifier = () => { | ||
@@ -298,11 +212,8 @@ if (this.jestObjGetterIdentifier) { | ||
} | ||
this.jestObjGetterIdentifier = | ||
program.scope.generateUidIdentifier('getJestObj'); | ||
program.unshiftContainer('body', [ | ||
createJestObjectGetter({ | ||
GETTER_NAME: this.jestObjGetterIdentifier.name, | ||
JEST_GLOBALS_MODULE_JEST_EXPORT_NAME, | ||
JEST_GLOBALS_MODULE_NAME | ||
}) | ||
]); | ||
this.jestObjGetterIdentifier = program.scope.generateUidIdentifier('getJestObj'); | ||
program.unshiftContainer('body', [createJestObjectGetter({ | ||
GETTER_NAME: this.jestObjGetterIdentifier.name, | ||
JEST_GLOBALS_MODULE_JEST_EXPORT_NAME, | ||
JEST_GLOBALS_MODULE_NAME | ||
})]); | ||
return this.jestObjGetterIdentifier; | ||
@@ -313,10 +224,5 @@ }; | ||
ExpressionStatement(exprStmt) { | ||
const jestObjInfo = extractJestObjExprIfHoistable( | ||
exprStmt.get('expression') | ||
); | ||
const jestObjInfo = extractJestObjExprIfHoistable(exprStmt.get('expression')); | ||
if (jestObjInfo) { | ||
const jestCallExpr = (0, _types().callExpression)( | ||
this.declareJestObjGetterIdentifier(), | ||
[] | ||
); | ||
const jestCallExpr = (0, _types().callExpression)(this.declareJestObjGetterIdentifier(), []); | ||
jestObjInfo.path.replaceWith(jestCallExpr); | ||
@@ -330,3 +236,5 @@ if (jestObjInfo.hoist) { | ||
// in `post` to make sure we come after an import transform and can unshift above the `require`s | ||
post({path: program}) { | ||
post({ | ||
path: program | ||
}) { | ||
visitBlock(program); | ||
@@ -338,6 +246,3 @@ program.traverse({ | ||
// use a temporary empty statement instead of the real first statement, which may itself be hoisted | ||
const [varsHoistPoint, callsHoistPoint] = block.unshiftContainer( | ||
'body', | ||
[(0, _types().emptyStatement)(), (0, _types().emptyStatement)()] | ||
); | ||
const [varsHoistPoint, callsHoistPoint] = block.unshiftContainer('body', [(0, _types().emptyStatement)(), (0, _types().emptyStatement)()]); | ||
block.traverse({ | ||
@@ -347,3 +252,3 @@ CallExpression: visitCallExpr, | ||
// do not traverse into nested blocks, or we'll hoist calls in there out to this block | ||
blacklist: ['BlockStatement'] | ||
denylist: ['BlockStatement'] | ||
}); | ||
@@ -369,3 +274,6 @@ callsHoistPoint.remove(); | ||
varDecl.parentPath.assertVariableDeclaration(); | ||
const {kind, declarations} = varDecl.parent; | ||
const { | ||
kind, | ||
declarations | ||
} = varDecl.parent; | ||
if (declarations.length === 1) { | ||
@@ -376,5 +284,3 @@ varDecl.parentPath.remove(); | ||
} | ||
varsHoistPoint.insertBefore( | ||
(0, _types().variableDeclaration)(kind, [varDecl.node]) | ||
); | ||
varsHoistPoint.insertBefore((0, _types().variableDeclaration)(kind, [varDecl.node])); | ||
} | ||
@@ -387,1 +293,6 @@ } | ||
/* eslint-enable */ | ||
})(); | ||
module.exports = __webpack_exports__; | ||
/******/ })() | ||
; |
{ | ||
"name": "babel-plugin-jest-hoist", | ||
"version": "29.6.3", | ||
"version": "30.0.0-alpha.0", | ||
"repository": { | ||
@@ -10,3 +10,3 @@ "type": "git", | ||
"engines": { | ||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0" | ||
"node": "^16.10.0 || ^18.12.0 || >=20.0.0" | ||
}, | ||
@@ -19,2 +19,4 @@ "license": "MIT", | ||
"types": "./build/index.d.ts", | ||
"require": "./build/index.js", | ||
"import": "./build/index.mjs", | ||
"default": "./build/index.js" | ||
@@ -34,2 +36,3 @@ }, | ||
"@babel/preset-typescript": "^7.0.0", | ||
"@prettier/sync": "^0.3.0", | ||
"@types/babel__template": "^7.0.2", | ||
@@ -39,3 +42,3 @@ "@types/node": "*", | ||
"babel-plugin-tester": "^11.0.2", | ||
"prettier": "^2.1.1" | ||
"prettier": "^3.0.3" | ||
}, | ||
@@ -45,3 +48,3 @@ "publishConfig": { | ||
}, | ||
"gitHead": "fb7d95c8af6e0d65a8b65348433d8a0ea0725b5b" | ||
"gitHead": "780ae28333df4d188b2ef78bd19d4ed5bc53562d" | ||
} |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
14355
6
9
288
2