fix: prevent asset conflicts between React and Grid.js versions

Add coexistence checks to all enqueue methods to prevent loading
both React and Grid.js assets simultaneously.

Changes:
- ReactAdmin.php: Only enqueue React assets when ?react=1
- Init.php: Skip Grid.js when React active on admin pages
- Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0
- Customer.php, Product.php, License.php: Add coexistence checks

Now the toggle between Classic and React versions works correctly.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
dwindown
2026-04-18 17:02:14 +07:00
parent bd9cdac02e
commit e8fbfb14c1
74973 changed files with 6658406 additions and 71 deletions

View File

@@ -0,0 +1,127 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isSupportedAccessor = exports.isStringNode = exports.isIdentifier = exports.getStringValue = exports.getAccessorValue = void 0;
var _utils = require("@typescript-eslint/utils");
/**
* A `Literal` with a `value` of type `string`.
*/
/**
* Checks if the given `node` is a `StringLiteral`.
*
* If a `value` is provided & the `node` is a `StringLiteral`,
* the `value` will be compared to that of the `StringLiteral`.
*
* @param {Node} node
* @param {V} [value]
*
* @return {node is StringLiteral<V>}
*
* @template V
*/
const isStringLiteral = (node, value) => node.type === _utils.AST_NODE_TYPES.Literal && typeof node.value === 'string' && (value === undefined || node.value === value);
/**
* Checks if the given `node` is a `TemplateLiteral`.
*
* Complex `TemplateLiteral`s are not considered specific, and so will return `false`.
*
* If a `value` is provided & the `node` is a `TemplateLiteral`,
* the `value` will be compared to that of the `TemplateLiteral`.
*
* @param {Node} node
* @param {V} [value]
*
* @return {node is TemplateLiteral<V>}
*
* @template V
*/
const isTemplateLiteral = (node, value) => node.type === _utils.AST_NODE_TYPES.TemplateLiteral && node.quasis.length === 1 && (
// bail out if not simple
value === undefined || node.quasis[0].value.raw === value);
/**
* Checks if the given `node` is a {@link StringNode}.
*
* @param {Node} node
* @param {V} [specifics]
*
* @return {node is StringNode}
*
* @template V
*/
const isStringNode = (node, specifics) => isStringLiteral(node, specifics) || isTemplateLiteral(node, specifics);
/**
* Gets the value of the given `StringNode`.
*
* If the `node` is a `TemplateLiteral`, the `raw` value is used;
* otherwise, `value` is returned instead.
*
* @param {StringNode<S>} node
*
* @return {S}
*
* @template S
*/
exports.isStringNode = isStringNode;
const getStringValue = node => isTemplateLiteral(node) ? node.quasis[0].value.raw : node.value;
/**
* An `Identifier` with a known `name` value - i.e `expect`.
*/
exports.getStringValue = getStringValue;
/**
* Checks if the given `node` is an `Identifier`.
*
* If a `name` is provided, & the `node` is an `Identifier`,
* the `name` will be compared to that of the `identifier`.
*
* @param {Node} node
* @param {V} [name]
*
* @return {node is KnownIdentifier<Name>}
*
* @template V
*/
const isIdentifier = (node, name) => node.type === _utils.AST_NODE_TYPES.Identifier && (name === undefined || node.name === name);
/**
* Checks if the given `node` is a "supported accessor".
*
* This means that it's a node can be used to access properties,
* and who's "value" can be statically determined.
*
* `MemberExpression` nodes most commonly contain accessors,
* but it's possible for other nodes to contain them.
*
* If a `value` is provided & the `node` is an `AccessorNode`,
* the `value` will be compared to that of the `AccessorNode`.
*
* Note that `value` here refers to the normalised value.
* The property that holds the value is not always called `name`.
*
* @param {Node} node
* @param {V} [value]
*
* @return {node is AccessorNode<V>}
*
* @template V
*/
exports.isIdentifier = isIdentifier;
const isSupportedAccessor = (node, value) => isIdentifier(node, value) || isStringNode(node, value);
/**
* Gets the value of the given `AccessorNode`,
* account for the different node types.
*
* @param {AccessorNode<S>} accessor
*
* @return {S}
*
* @template S
*/
exports.isSupportedAccessor = isSupportedAccessor;
const getAccessorValue = accessor => accessor.type === _utils.AST_NODE_TYPES.Identifier ? accessor.name : getStringValue(accessor);
exports.getAccessorValue = getAccessorValue;

View File

@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.detectJestVersion = void 0;
let cachedJestVersion = null;
const detectJestVersion = () => {
if (cachedJestVersion) {
return cachedJestVersion;
}
try {
const jestPath = require.resolve('jest/package.json');
const jestPackageJson =
// eslint-disable-next-line @typescript-eslint/no-require-imports
require(jestPath);
if (jestPackageJson.version) {
const [majorVersion] = jestPackageJson.version.split('.');
return cachedJestVersion = parseInt(majorVersion, 10);
}
} catch {}
throw new Error('Unable to detect Jest version - please ensure jest package is installed, or otherwise set version explicitly');
};
exports.detectJestVersion = detectJestVersion;

View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.followTypeAssertionChain = void 0;
var _utils = require("@typescript-eslint/utils");
const isTypeCastExpression = node => node.type === _utils.AST_NODE_TYPES.TSAsExpression || node.type === _utils.AST_NODE_TYPES.TSTypeAssertion;
const followTypeAssertionChain = expression => isTypeCastExpression(expression) ? followTypeAssertionChain(expression.expression) : expression;
exports.followTypeAssertionChain = followTypeAssertionChain;

View File

@@ -0,0 +1,60 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _accessors = require("./accessors");
Object.keys(_accessors).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _accessors[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _accessors[key];
}
});
});
var _detectJestVersion = require("./detectJestVersion");
Object.keys(_detectJestVersion).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _detectJestVersion[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _detectJestVersion[key];
}
});
});
var _followTypeAssertionChain = require("./followTypeAssertionChain");
Object.keys(_followTypeAssertionChain).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _followTypeAssertionChain[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _followTypeAssertionChain[key];
}
});
});
var _misc = require("./misc");
Object.keys(_misc).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _misc[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _misc[key];
}
});
});
var _parseJestFnCall = require("./parseJestFnCall");
Object.keys(_parseJestFnCall).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _parseJestFnCall[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _parseJestFnCall[key];
}
});
});

201
node_modules/eslint-plugin-jest/lib/rules/utils/misc.js generated vendored Normal file
View File

@@ -0,0 +1,201 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getFirstMatcherArg = exports.getFilename = exports.getDeclaredVariables = exports.getAncestors = exports.findTopMostCallExpression = exports.createRule = exports.TestCaseName = exports.ModifierName = exports.HookName = exports.EqualityMatcher = exports.DescribeAlias = void 0;
exports.getNodeName = getNodeName;
exports.replaceAccessorFixer = exports.removeExtraArgumentsFixer = exports.isFunction = exports.isBooleanLiteral = exports.hasOnlyOneArgument = exports.getTestCallExpressionsFromDeclaredVariables = exports.getSourceCode = exports.getScope = void 0;
var _path = require("path");
var _utils = require("@typescript-eslint/utils");
var _package = require("../../../package.json");
var _accessors = require("./accessors");
var _followTypeAssertionChain = require("./followTypeAssertionChain");
var _parseJestFnCall = require("./parseJestFnCall");
const REPO_URL = 'https://github.com/jest-community/eslint-plugin-jest';
const createRule = exports.createRule = _utils.ESLintUtils.RuleCreator(name => {
const ruleName = (0, _path.parse)(name).name;
return `${REPO_URL}/blob/v${_package.version}/docs/rules/${ruleName}.md`;
});
/**
* Represents a `MemberExpression` with a "known" `property`.
*/
/**
* Represents a `CallExpression` with a "known" `property` accessor.
*
* i.e `KnownCallExpression<'includes'>` represents `.includes()`.
*/
/**
* Represents a `MemberExpression` with a "known" `property`, that is called.
*
* This is `KnownCallExpression` from the perspective of the `MemberExpression` node.
*/
/**
* Represents a `CallExpression` with a single argument.
*/
/**
* Guards that the given `call` has only one `argument`.
*
* @param {CallExpression} call
*
* @return {call is CallExpressionWithSingleArgument}
*/
const hasOnlyOneArgument = call => call.arguments.length === 1;
exports.hasOnlyOneArgument = hasOnlyOneArgument;
let DescribeAlias = exports.DescribeAlias = /*#__PURE__*/function (DescribeAlias) {
DescribeAlias["describe"] = "describe";
DescribeAlias["fdescribe"] = "fdescribe";
DescribeAlias["xdescribe"] = "xdescribe";
return DescribeAlias;
}({});
let TestCaseName = exports.TestCaseName = /*#__PURE__*/function (TestCaseName) {
TestCaseName["fit"] = "fit";
TestCaseName["it"] = "it";
TestCaseName["test"] = "test";
TestCaseName["xit"] = "xit";
TestCaseName["xtest"] = "xtest";
return TestCaseName;
}({});
let HookName = exports.HookName = /*#__PURE__*/function (HookName) {
HookName["beforeAll"] = "beforeAll";
HookName["beforeEach"] = "beforeEach";
HookName["afterAll"] = "afterAll";
HookName["afterEach"] = "afterEach";
return HookName;
}({});
let ModifierName = exports.ModifierName = /*#__PURE__*/function (ModifierName) {
ModifierName["not"] = "not";
ModifierName["rejects"] = "rejects";
ModifierName["resolves"] = "resolves";
return ModifierName;
}({});
let EqualityMatcher = exports.EqualityMatcher = /*#__PURE__*/function (EqualityMatcher) {
EqualityMatcher["toBe"] = "toBe";
EqualityMatcher["toEqual"] = "toEqual";
EqualityMatcher["toStrictEqual"] = "toStrictEqual";
return EqualityMatcher;
}({});
const joinNames = (a, b) => a && b ? `${a}.${b}` : null;
function getNodeName(node) {
if ((0, _accessors.isSupportedAccessor)(node)) {
return (0, _accessors.getAccessorValue)(node);
}
switch (node.type) {
case _utils.AST_NODE_TYPES.TaggedTemplateExpression:
return getNodeName(node.tag);
case _utils.AST_NODE_TYPES.MemberExpression:
return joinNames(getNodeName(node.object), getNodeName(node.property));
case _utils.AST_NODE_TYPES.NewExpression:
case _utils.AST_NODE_TYPES.CallExpression:
return getNodeName(node.callee);
}
return null;
}
const isFunction = node => node.type === _utils.AST_NODE_TYPES.FunctionExpression || node.type === _utils.AST_NODE_TYPES.ArrowFunctionExpression;
exports.isFunction = isFunction;
const getTestCallExpressionsFromDeclaredVariables = (declaredVariables, context) => {
return declaredVariables.reduce((acc, {
references
}) => acc.concat(references.map(({
identifier
}) => identifier.parent).filter(node => (node === null || node === void 0 ? void 0 : node.type) === _utils.AST_NODE_TYPES.CallExpression && (0, _parseJestFnCall.isTypeOfJestFnCall)(node, context, ['test']))), []);
};
/**
* Replaces an accessor node with the given `text`, surrounding it in quotes if required.
*
* This ensures that fixes produce valid code when replacing both dot-based and
* bracket-based property accessors.
*/
exports.getTestCallExpressionsFromDeclaredVariables = getTestCallExpressionsFromDeclaredVariables;
const replaceAccessorFixer = (fixer, node, text) => {
return fixer.replaceText(node, node.type === _utils.AST_NODE_TYPES.Identifier ? text : `'${text}'`);
};
exports.replaceAccessorFixer = replaceAccessorFixer;
const removeExtraArgumentsFixer = (fixer, context, func, from) => {
const firstArg = func.arguments[from];
const lastArg = func.arguments[func.arguments.length - 1];
const sourceCode = getSourceCode(context);
let tokenAfterLastParam = sourceCode.getTokenAfter(lastArg);
if (tokenAfterLastParam.value === ',') {
tokenAfterLastParam = sourceCode.getTokenAfter(tokenAfterLastParam);
}
return fixer.removeRange([firstArg.range[0], tokenAfterLastParam.range[0]]);
};
exports.removeExtraArgumentsFixer = removeExtraArgumentsFixer;
const findTopMostCallExpression = node => {
let topMostCallExpression = node;
let {
parent
} = node;
while (parent) {
if (parent.type === _utils.AST_NODE_TYPES.CallExpression) {
topMostCallExpression = parent;
parent = parent.parent;
continue;
}
if (parent.type !== _utils.AST_NODE_TYPES.MemberExpression) {
break;
}
parent = parent.parent;
}
return topMostCallExpression;
};
exports.findTopMostCallExpression = findTopMostCallExpression;
const isBooleanLiteral = node => node.type === _utils.AST_NODE_TYPES.Literal && typeof node.value === 'boolean';
exports.isBooleanLiteral = isBooleanLiteral;
const getFirstMatcherArg = expectFnCall => {
const [firstArg] = expectFnCall.args;
if (firstArg.type === _utils.AST_NODE_TYPES.SpreadElement) {
return firstArg;
}
return (0, _followTypeAssertionChain.followTypeAssertionChain)(firstArg);
};
/* istanbul ignore next */
exports.getFirstMatcherArg = getFirstMatcherArg;
const getFilename = context => {
return 'filename' in context ? context.filename : context.getFilename();
};
/* istanbul ignore next */
exports.getFilename = getFilename;
const getSourceCode = context => {
return 'sourceCode' in context ? context.sourceCode : context.getSourceCode();
};
/* istanbul ignore next */
exports.getSourceCode = getSourceCode;
const getScope = (context, node) => {
const sourceCode = getSourceCode(context);
if ('getScope' in sourceCode) {
return sourceCode.getScope(node);
}
return context.getScope();
};
/* istanbul ignore next */
exports.getScope = getScope;
const getAncestors = (context, node) => {
const sourceCode = getSourceCode(context);
if ('getAncestors' in sourceCode) {
return sourceCode.getAncestors(node);
}
return context.getAncestors();
};
/* istanbul ignore next */
exports.getAncestors = getAncestors;
const getDeclaredVariables = (context, node) => {
const sourceCode = getSourceCode(context);
if ('getDeclaredVariables' in sourceCode) {
return sourceCode.getDeclaredVariables(node);
}
return context.getDeclaredVariables(node);
};
exports.getDeclaredVariables = getDeclaredVariables;

View File

@@ -0,0 +1,332 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getNodeChain = getNodeChain;
exports.resolveScope = exports.parseJestFnCallWithReason = exports.parseJestFnCall = exports.isTypeOfJestFnCall = void 0;
var _utils = require("@typescript-eslint/utils");
var _utils2 = require("../utils");
const isTypeOfJestFnCall = (node, context, types) => {
const jestFnCall = parseJestFnCall(node, context);
return jestFnCall !== null && types.includes(jestFnCall.type);
};
exports.isTypeOfJestFnCall = isTypeOfJestFnCall;
const joinChains = (a, b) => a && b ? [...a, ...b] : null;
function getNodeChain(node) {
if ((0, _utils2.isSupportedAccessor)(node)) {
return [node];
}
switch (node.type) {
case _utils.AST_NODE_TYPES.TaggedTemplateExpression:
return getNodeChain(node.tag);
case _utils.AST_NODE_TYPES.MemberExpression:
return joinChains(getNodeChain(node.object), getNodeChain(node.property));
case _utils.AST_NODE_TYPES.CallExpression:
return getNodeChain(node.callee);
}
return null;
}
const determineJestFnType = name => {
if (name === 'expect') {
return 'expect';
}
if (name === 'jest') {
return 'jest';
}
if (_utils2.DescribeAlias.hasOwnProperty(name)) {
return 'describe';
}
if (_utils2.TestCaseName.hasOwnProperty(name)) {
return 'test';
}
/* istanbul ignore else */
if (_utils2.HookName.hasOwnProperty(name)) {
return 'hook';
}
/* istanbul ignore next */
return 'unknown';
};
const ValidJestFnCallChains = ['afterAll', 'afterEach', 'beforeAll', 'beforeEach', 'describe', 'describe.each', 'describe.only', 'describe.only.each', 'describe.skip', 'describe.skip.each', 'fdescribe', 'fdescribe.each', 'xdescribe', 'xdescribe.each', 'it', 'it.concurrent', 'it.concurrent.failing', 'it.concurrent.each', 'it.concurrent.failing.each', 'it.concurrent.failing.only.each', 'it.concurrent.failing.skip.each', 'it.concurrent.only.each', 'it.concurrent.skip.each', 'it.each', 'it.failing', 'it.failing.each', 'it.only', 'it.only.each', 'it.only.failing', 'it.only.failing.each', 'it.skip', 'it.skip.each', 'it.skip.failing', 'it.skip.failing.each', 'it.todo', 'fit', 'fit.each', 'fit.failing', 'fit.failing.each', 'xit', 'xit.each', 'xit.failing', 'xit.failing.each', 'test', 'test.concurrent', 'test.concurrent.failing', 'test.concurrent.each', 'test.concurrent.failing.each', 'test.concurrent.failing.only.each', 'test.concurrent.failing.skip.each', 'test.concurrent.only.each', 'test.concurrent.skip.each', 'test.each', 'test.failing', 'test.failing.each', 'test.only', 'test.only.each', 'test.only.failing', 'test.only.failing.each', 'test.skip', 'test.skip.each', 'test.skip.failing', 'test.skip.failing.each', 'test.todo', 'xtest', 'xtest.each', 'xtest.failing', 'xtest.failing.each'];
const resolvePossibleAliasedGlobal = (global, context) => {
var _context$settings$jes;
const globalAliases = ((_context$settings$jes = context.settings.jest) === null || _context$settings$jes === void 0 ? void 0 : _context$settings$jes.globalAliases) ?? {};
const alias = Object.entries(globalAliases).find(([, aliases]) => aliases.includes(global));
if (alias) {
return alias[0];
}
return null;
};
const parseJestFnCallCache = new WeakMap();
const parseJestFnCall = (node, context) => {
const jestFnCall = parseJestFnCallWithReason(node, context);
if (typeof jestFnCall === 'string') {
return null;
}
return jestFnCall;
};
exports.parseJestFnCall = parseJestFnCall;
const parseJestFnCallWithReason = (node, context) => {
let parsedJestFnCall = parseJestFnCallCache.get(node);
if (parsedJestFnCall) {
return parsedJestFnCall;
}
parsedJestFnCall = parseJestFnCallWithReasonInner(node, context);
parseJestFnCallCache.set(node, parsedJestFnCall);
return parsedJestFnCall;
};
exports.parseJestFnCallWithReason = parseJestFnCallWithReason;
const parseJestFnCallWithReasonInner = (node, context) => {
var _node$parent2, _node$parent3;
const chain = getNodeChain(node);
if (!(chain !== null && chain !== void 0 && chain.length)) {
return null;
}
const [first, ...rest] = chain;
const lastLink = (0, _utils2.getAccessorValue)(chain[chain.length - 1]);
// if we're an `each()`, ensure we're the outer CallExpression (i.e `.each()()`)
if (lastLink === 'each') {
if (node.callee.type !== _utils.AST_NODE_TYPES.CallExpression && node.callee.type !== _utils.AST_NODE_TYPES.TaggedTemplateExpression) {
return null;
}
}
if (node.callee.type === _utils.AST_NODE_TYPES.TaggedTemplateExpression && lastLink !== 'each') {
return null;
}
const resolved = resolveToJestFn(context, first);
// we're not a jest function
if (!resolved) {
return null;
}
const name = resolved.original ?? resolved.local;
const links = [name, ...rest.map(link => (0, _utils2.getAccessorValue)(link))];
if (name !== 'jest' && name !== 'expect' && !ValidJestFnCallChains.includes(links.join('.'))) {
return null;
}
const parsedJestFnCall = {
name,
head: {
...resolved,
node: first
},
// every member node must have a member expression as their parent
// in order to be part of the call chain we're parsing
members: rest
};
const type = determineJestFnType(name);
if (type === 'expect') {
const result = parseJestExpectCall(parsedJestFnCall);
// if the `expect` call chain is not valid, only report on the topmost node
// since all members in the chain are likely to get flagged for some reason
if (typeof result === 'string' && (0, _utils2.findTopMostCallExpression)(node) !== node) {
return null;
}
if (result === 'matcher-not-found') {
var _node$parent;
if (((_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.type) === _utils.AST_NODE_TYPES.MemberExpression) {
return 'matcher-not-called';
}
}
return result;
}
// check that every link in the chain except the last is a member expression
if (chain.slice(0, chain.length - 1).some(nod => {
var _nod$parent;
return ((_nod$parent = nod.parent) === null || _nod$parent === void 0 ? void 0 : _nod$parent.type) !== _utils.AST_NODE_TYPES.MemberExpression;
})) {
return null;
}
// ensure that we're at the "top" of the function call chain otherwise when
// parsing e.g. x().y.z(), we'll incorrectly find & parse "x()" even though
// the full chain is not a valid jest function call chain
if (((_node$parent2 = node.parent) === null || _node$parent2 === void 0 ? void 0 : _node$parent2.type) === _utils.AST_NODE_TYPES.CallExpression || ((_node$parent3 = node.parent) === null || _node$parent3 === void 0 ? void 0 : _node$parent3.type) === _utils.AST_NODE_TYPES.MemberExpression) {
return null;
}
return {
...parsedJestFnCall,
type
};
};
const findModifiersAndMatcher = members => {
const modifiers = [];
for (const member of members) {
var _member$parent, _member$parent$parent;
// check if the member is being called, which means it is the matcher
// (and also the end of the entire "expect" call chain)
if (((_member$parent = member.parent) === null || _member$parent === void 0 ? void 0 : _member$parent.type) === _utils.AST_NODE_TYPES.MemberExpression && ((_member$parent$parent = member.parent.parent) === null || _member$parent$parent === void 0 ? void 0 : _member$parent$parent.type) === _utils.AST_NODE_TYPES.CallExpression) {
return {
matcher: member,
args: member.parent.parent.arguments,
modifiers
};
}
// otherwise, it should be a modifier
const name = (0, _utils2.getAccessorValue)(member);
if (modifiers.length === 0) {
// the first modifier can be any of the three modifiers
if (!_utils2.ModifierName.hasOwnProperty(name)) {
return 'modifier-unknown';
}
} else if (modifiers.length === 1) {
// the second modifier can only be "not"
if (name !== _utils2.ModifierName.not) {
return 'modifier-unknown';
}
const firstModifier = (0, _utils2.getAccessorValue)(modifiers[0]);
// and the first modifier has to be either "resolves" or "rejects"
if (firstModifier !== _utils2.ModifierName.resolves && firstModifier !== _utils2.ModifierName.rejects) {
return 'modifier-unknown';
}
} else {
return 'modifier-unknown';
}
modifiers.push(member);
}
// this will only really happen if there are no members
return 'matcher-not-found';
};
const parseJestExpectCall = typelessParsedJestFnCall => {
const modifiersAndMatcher = findModifiersAndMatcher(typelessParsedJestFnCall.members);
if (typeof modifiersAndMatcher === 'string') {
return modifiersAndMatcher;
}
return {
...typelessParsedJestFnCall,
type: 'expect',
...modifiersAndMatcher
};
};
const describeImportDefAsImport = def => {
if (def.parent.type === _utils.AST_NODE_TYPES.TSImportEqualsDeclaration) {
return null;
}
if (def.node.type !== _utils.AST_NODE_TYPES.ImportSpecifier) {
return null;
}
// we only care about value imports
if (def.parent.importKind === 'type') {
return null;
}
return {
source: def.parent.source.value,
imported: def.node.imported.name,
local: def.node.local.name
};
};
/**
* Attempts to find the node that represents the import source for the
* given expression node, if it looks like it's an import.
*
* If no such node can be found (e.g. because the expression doesn't look
* like an import), then `null` is returned instead.
*/
const findImportSourceNode = node => {
if (node.type === _utils.AST_NODE_TYPES.AwaitExpression) {
if (node.argument.type === _utils.AST_NODE_TYPES.ImportExpression) {
return node.argument.source;
}
return null;
}
if (node.type === _utils.AST_NODE_TYPES.CallExpression && (0, _utils2.isIdentifier)(node.callee, 'require')) {
return node.arguments[0] ?? null;
}
return null;
};
const describeVariableDefAsImport = def => {
var _def$name$parent;
// make sure that we've actually being assigned a value
if (!def.node.init) {
return null;
}
const sourceNode = findImportSourceNode(def.node.init);
if (!sourceNode || !(0, _utils2.isStringNode)(sourceNode)) {
return null;
}
if (((_def$name$parent = def.name.parent) === null || _def$name$parent === void 0 ? void 0 : _def$name$parent.type) !== _utils.AST_NODE_TYPES.Property) {
return null;
}
if (!(0, _utils2.isSupportedAccessor)(def.name.parent.key)) {
return null;
}
return {
source: (0, _utils2.getStringValue)(sourceNode),
imported: (0, _utils2.getAccessorValue)(def.name.parent.key),
local: def.name.name
};
};
/**
* Attempts to describe a definition as an import if possible.
*
* If the definition is an import binding, it's described as you'd expect.
* If the definition is a variable, then we try and determine if it's either
* a dynamic `import()` or otherwise a call to `require()`.
*
* If it's neither of these, `null` is returned to indicate that the definition
* is not describable as an import of any kind.
*/
const describePossibleImportDef = def => {
if (def.type === 'Variable') {
return describeVariableDefAsImport(def);
}
if (def.type === 'ImportBinding') {
return describeImportDefAsImport(def);
}
return null;
};
const resolveScope = (scope, identifier) => {
let currentScope = scope;
while (currentScope !== null) {
const ref = currentScope.set.get(identifier);
if (ref && ref.defs.length > 0) {
const def = ref.defs[ref.defs.length - 1];
const importDetails = describePossibleImportDef(def);
if ((importDetails === null || importDetails === void 0 ? void 0 : importDetails.local) === identifier) {
return importDetails;
}
return 'local';
}
currentScope = currentScope.upper;
}
return null;
};
exports.resolveScope = resolveScope;
const resolveToJestFn = (context, accessor) => {
const identifier = (0, _utils2.getAccessorValue)(accessor);
const maybeImport = resolveScope((0, _utils2.getScope)(context, accessor), identifier);
// the identifier was found as a local variable or function declaration
// meaning it's not a function from jest
if (maybeImport === 'local') {
return null;
}
if (maybeImport) {
// the identifier is imported from @jest/globals,
// so return the original import name
if (maybeImport.source === '@jest/globals') {
return {
original: maybeImport.imported,
local: maybeImport.local,
type: 'import'
};
}
return null;
}
return {
original: resolvePossibleAliasedGlobal(identifier, context),
local: identifier,
type: 'global'
};
};