123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- 'use strict';
- /**
- * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- *
- */
- function invariant(condition, message) {
- if (!condition) {
- throw new Error('babel-plugin-jest-hoist: ' + message);
- }
- }
- // 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.
- const WHITELISTED_IDENTIFIERS = {
- Array: true,
- ArrayBuffer: true,
- Boolean: true,
- DataView: true,
- Date: true,
- Error: true,
- EvalError: true,
- Float32Array: true,
- Float64Array: true,
- Function: true,
- Generator: true,
- GeneratorFunction: true,
- Infinity: true,
- Int16Array: true,
- Int32Array: true,
- Int8Array: true,
- InternalError: true,
- Intl: true,
- JSON: true,
- Map: true,
- Math: true,
- NaN: true,
- Number: true,
- Object: true,
- Promise: true,
- Proxy: true,
- RangeError: true,
- ReferenceError: true,
- Reflect: true,
- RegExp: true,
- Set: true,
- String: true,
- Symbol: true,
- SyntaxError: true,
- TypeError: true,
- URIError: true,
- Uint16Array: true,
- Uint32Array: true,
- Uint8Array: true,
- Uint8ClampedArray: true,
- WeakMap: true,
- WeakSet: true,
- arguments: true,
- console: true,
- expect: true,
- isNaN: true,
- jest: true,
- parseFloat: true,
- parseInt: true,
- require: true,
- undefined: true
- };
- Object.keys(global).forEach(name => (WHITELISTED_IDENTIFIERS[name] = true));
- const JEST_GLOBAL = {name: 'jest'};
- const IDVisitor = {
- ReferencedIdentifier(path) {
- this.ids.add(path);
- },
- blacklist: ['TypeAnnotation']
- };
- const FUNCTIONS = Object.create(null);
- FUNCTIONS.mock = args => {
- if (args.length === 1) {
- return args[0].isStringLiteral() || args[0].isLiteral();
- } else if (args.length === 2 || args.length === 3) {
- const moduleFactory = args[1];
- invariant(
- moduleFactory.isFunction(),
- 'The second argument of `jest.mock` must be an inline function.'
- );
- const ids = new Set();
- const parentScope = moduleFactory.parentPath.scope;
- moduleFactory.traverse(IDVisitor, {ids});
- for (const id of ids) {
- const name = id.node.name;
- let found = false;
- let scope = id.scope;
- while (scope !== parentScope) {
- if (scope.bindings[name]) {
- found = true;
- break;
- }
- scope = scope.parent;
- }
- if (!found) {
- invariant(
- (scope.hasGlobal(name) && WHITELISTED_IDENTIFIERS[name]) ||
- /^mock/i.test(name) ||
- // Allow istanbul's coverage variable to pass.
- /^(?:__)?cov/.test(name),
- 'The module factory of `jest.mock()` is not allowed to ' +
- 'reference any out-of-scope variables.\n' +
- 'Invalid variable access: ' +
- name +
- '\n' +
- 'Whitelisted objects: ' +
- Object.keys(WHITELISTED_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.'
- );
- }
- }
- return true;
- }
- return false;
- };
- FUNCTIONS.unmock = args => args.length === 1 && args[0].isStringLiteral();
- FUNCTIONS.deepUnmock = args => args.length === 1 && args[0].isStringLiteral();
- FUNCTIONS.disableAutomock = FUNCTIONS.enableAutomock = args =>
- args.length === 0;
- module.exports = () => {
- const isJest = callee =>
- callee.get('object').isIdentifier(JEST_GLOBAL) ||
- (callee.isMemberExpression() && isJest(callee.get('object')));
- const shouldHoistExpression = expr => {
- if (!expr.isCallExpression()) {
- return false;
- }
- const callee = expr.get('callee');
- const object = callee.get('object');
- const property = callee.get('property');
- return (
- property.isIdentifier() &&
- FUNCTIONS[property.node.name] &&
- (object.isIdentifier(JEST_GLOBAL) ||
- (callee.isMemberExpression() && shouldHoistExpression(object))) &&
- FUNCTIONS[property.node.name](expr.get('arguments'))
- );
- };
- return {
- visitor: {
- ExpressionStatement(path) {
- if (shouldHoistExpression(path.get('expression'))) {
- path.node._blockHoist = Infinity;
- }
- }
- }
- };
- };
|