| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 | 'use strict';const {isParenthesized, getStaticValue} = require('@eslint-community/eslint-utils');const {checkVueTemplate} = require('./utils/rule.js');const {methodCallSelector} = require('./selectors/index.js');const {isRegexLiteral, isNewExpression} = require('./ast/index.js');const {isBooleanNode} = require('./utils/boolean.js');const shouldAddParenthesesToMemberExpressionObject = require('./utils/should-add-parentheses-to-member-expression-object.js');const REGEXP_EXEC = 'regexp-exec';const STRING_MATCH = 'string-match';const SUGGESTION = 'suggestion';const messages = {	[REGEXP_EXEC]: 'Prefer `.test(…)` over `.exec(…)`.',	[STRING_MATCH]: 'Prefer `RegExp#test(…)` over `String#match(…)`.',	[SUGGESTION]: 'Switch to `RegExp#test(…)`.',};const cases = [	{		type: REGEXP_EXEC,		selector: methodCallSelector({			method: 'exec',			argumentsLength: 1,		}),		getNodes: node => ({			stringNode: node.arguments[0],			methodNode: node.callee.property,			regexpNode: node.callee.object,		}),		fix: (fixer, {methodNode}) => fixer.replaceText(methodNode, 'test'),	},	{		type: STRING_MATCH,		selector: methodCallSelector({			method: 'match',			argumentsLength: 1,		}),		getNodes: node => ({			stringNode: node.callee.object,			methodNode: node.callee.property,			regexpNode: node.arguments[0],		}),		* fix(fixer, {stringNode, methodNode, regexpNode}, sourceCode) {			yield fixer.replaceText(methodNode, 'test');			let stringText = sourceCode.getText(stringNode);			if (				!isParenthesized(regexpNode, sourceCode)				// Only `SequenceExpression` need add parentheses				&& stringNode.type === 'SequenceExpression'			) {				stringText = `(${stringText})`;			}			yield fixer.replaceText(regexpNode, stringText);			let regexpText = sourceCode.getText(regexpNode);			if (				!isParenthesized(stringNode, sourceCode)				&& shouldAddParenthesesToMemberExpressionObject(regexpNode, sourceCode)			) {				regexpText = `(${regexpText})`;			}			// The nodes that pass `isBooleanNode` cannot have an ASI problem.			yield fixer.replaceText(stringNode, regexpText);		},	},];const isRegExpNode = node => isRegexLiteral(node) || isNewExpression(node, {name: 'RegExp'});const isRegExpWithoutGlobalFlag = (node, scope) => {	const staticResult = getStaticValue(node, scope);	// Don't know if there is `g` flag	if (!staticResult) {		return false;	}	const {value} = staticResult;	return (		Object.prototype.toString.call(value) === '[object RegExp]'		&& !value.global	);};/** @param {import('eslint').Rule.RuleContext} context */const create = context => Object.fromEntries(	cases.map(checkCase => [		checkCase.selector,		node => {			if (!isBooleanNode(node)) {				return;			}			const {type, getNodes, fix} = checkCase;			const nodes = getNodes(node);			const {methodNode, regexpNode} = nodes;			if (regexpNode.type === 'Literal' && !regexpNode.regex) {				return;			}			const problem = {				node: type === REGEXP_EXEC ? methodNode : node,				messageId: type,			};			const fixFunction = fixer => fix(fixer, nodes, context.getSourceCode());			if (				isRegExpNode(regexpNode)				|| isRegExpWithoutGlobalFlag(regexpNode, context.getScope())			) {				problem.fix = fixFunction;			} else {				problem.suggest = [					{						messageId: SUGGESTION,						fix: fixFunction,					},				];			}			return problem;		},	]),);/** @type {import('eslint').Rule.RuleModule} */module.exports = {	create: checkVueTemplate(create),	meta: {		type: 'suggestion',		docs: {			description: 'Prefer `RegExp#test()` over `String#match()` and `RegExp#exec()`.',		},		fixable: 'code',		hasSuggestions: true,		messages,	},};
 |