📄 prefer-regexp-exec¶
📊 Analysis Summary¶
| Metric | Count |
|---|---|
| 🔧 Functions | 5 |
| 📦 Imports | 8 |
| 🎯 Enums | 1 |
📚 Table of Contents¶
🛠️ File Location:¶
📂 packages/eslint-plugin/src/rules/prefer-regexp-exec.ts
📤 Default Export¶
| Property | Value |
|---|---|
name |
'prefer-regexp-exec' |
meta.type |
'suggestion' |
meta.docs.description |
'Enforce RegExp#exec over String#match if no global flag is provided' |
meta.docs.recommended |
'stylistic' |
meta.docs.requiresTypeChecking |
true |
meta.fixable |
'code' |
meta.messages.regExpExecOverStringMatch |
'Use the RegExp#exec() method instead.' |
meta.schema |
[] |
defaultOptions |
[] |
Entry point: create — documented under Functions.
📦 Imports¶
| Name | Source |
|---|---|
TSESTree |
@typescript-eslint/utils |
AST_NODE_TYPES |
@typescript-eslint/utils |
createRule |
../util |
getParserServices |
../util |
getStaticValue |
../util |
getTypeName |
../util |
getWrappingFixer |
../util |
isStaticMemberAccessOfValue |
../util |
Functions¶
create(context: any): { 'CallExpression[arguments.length=1] > MemberExpression'(m…¶
Parameters:
contextany
Returns: { 'CallExpression[arguments.length=1] > MemberExpression'(memberNode: TSESTree.MemberExpression): void; }
Calls:
context.sourceCode.getScopegetParserServices (from ../util)services.program.getTypeCheckergetTypeName (from ../util)isRegExpTypeisStringTypenode.arguments.atgetStaticValue (from ../util)flagsValue.value.includesisStaticMemberAccessOfValue (from ../util)services.getTypeAtLocationdefinitelyDoesNotContainGlobalFlagargumentValue.value.flags.includesRegExpcontext.reportgetWrappingFixer (from ../util)regExp.toStringcollectArgumentTypestsutils.unionConstituents
Internal Comments:
/**
* Check if a given node type is a string.
* @param type The node type to check.
*/
/**
* Check if a given node type is a RegExp.
* @param type The node type to check.
*/
/**
* Returns true if and only if we have syntactic proof that the /g flag is
* absent. Returns false in all other cases (i.e. it still might or might
* not contain the global flag).
*/
// Don't report regular expressions with global flag.
Code
create(context) {
const globalScope = context.sourceCode.getScope(context.sourceCode.ast);
const services = getParserServices(context);
const checker = services.program.getTypeChecker();
/**
* Check if a given node type is a string.
* @param type The node type to check.
*/
function isStringType(type: ts.Type): boolean {
return getTypeName(checker, type) === 'string';
}
/**
* Check if a given node type is a RegExp.
* @param type The node type to check.
*/
function isRegExpType(type: ts.Type): boolean {
return getTypeName(checker, type) === 'RegExp';
}
function collectArgumentTypes(types: ts.Type[]): ArgumentType {
let result = ArgumentType.Other;
for (const type of types) {
if (isRegExpType(type)) {
result |= ArgumentType.RegExp;
} else if (isStringType(type)) {
result |= ArgumentType.String;
}
}
return result;
}
/**
* Returns true if and only if we have syntactic proof that the /g flag is
* absent. Returns false in all other cases (i.e. it still might or might
* not contain the global flag).
*/
function definitelyDoesNotContainGlobalFlag(
node: TSESTree.CallExpressionArgument,
): boolean {
if (
(node.type === AST_NODE_TYPES.CallExpression ||
node.type === AST_NODE_TYPES.NewExpression) &&
node.callee.type === AST_NODE_TYPES.Identifier &&
node.callee.name === 'RegExp'
) {
const flags = node.arguments.at(1);
if (!flags) {
return true;
}
const flagsValue = getStaticValue(flags, globalScope);
return (
!!flagsValue &&
(typeof flagsValue.value !== 'string' ||
!flagsValue.value.includes('g'))
);
}
return false;
}
return {
'CallExpression[arguments.length=1] > MemberExpression'(
memberNode: TSESTree.MemberExpression,
): void {
if (!isStaticMemberAccessOfValue(memberNode, context, 'match')) {
return;
}
const objectNode = memberNode.object;
const callNode = memberNode.parent as TSESTree.CallExpression;
const [argumentNode] = callNode.arguments;
const argumentValue = getStaticValue(argumentNode, globalScope);
if (!isStringType(services.getTypeAtLocation(objectNode))) {
return;
}
// Don't report regular expressions with global flag.
if (
(!argumentValue &&
!definitelyDoesNotContainGlobalFlag(argumentNode)) ||
(argumentValue &&
argumentValue.value instanceof RegExp &&
argumentValue.value.flags.includes('g'))
) {
return;
}
if (
argumentNode.type === AST_NODE_TYPES.Literal &&
typeof argumentNode.value === 'string'
) {
let regExp: RegExp;
try {
regExp = RegExp(argumentNode.value);
} catch {
return;
}
return context.report({
node: memberNode.property,
messageId: 'regExpExecOverStringMatch',
fix: getWrappingFixer({
node: callNode,
innerNode: [objectNode],
sourceCode: context.sourceCode,
wrap: objectCode => `${regExp.toString()}.exec(${objectCode})`,
}),
});
}
const argumentType = services.getTypeAtLocation(argumentNode);
const argumentTypes = collectArgumentTypes(
tsutils.unionConstituents(argumentType),
);
switch (argumentTypes) {
case ArgumentType.RegExp:
return context.report({
node: memberNode.property,
messageId: 'regExpExecOverStringMatch',
fix: getWrappingFixer({
node: callNode,
innerNode: [objectNode, argumentNode],
sourceCode: context.sourceCode,
wrap: (objectCode, argumentCode) =>
`${argumentCode}.exec(${objectCode})`,
}),
});
case ArgumentType.String:
return context.report({
node: memberNode.property,
messageId: 'regExpExecOverStringMatch',
fix: getWrappingFixer({
node: callNode,
innerNode: [objectNode, argumentNode],
sourceCode: context.sourceCode,
wrap: (objectCode, argumentCode) =>
`RegExp(${argumentCode}).exec(${objectCode})`,
}),
});
}
},
};
}
Internal helpers¶
Declared inside another function in this file.
isStringType(type: ts.Type): boolean¶
Check if a given node type is a string.
Parameters:
typeany: The node type to check.
Calls:
getTypeName (from ../util)
Code
isRegExpType(type: ts.Type): boolean¶
Check if a given node type is a RegExp.
Parameters:
typeany: The node type to check.
Calls:
getTypeName (from ../util)
Code
collectArgumentTypes(types: ts.Type[]): ArgumentType¶
Parameters:
typests.Type[]
Returns: ArgumentType
Calls:
isRegExpTypeisStringType
Code
definitelyDoesNotContainGlobalFlag(node: TSESTree.CallExpressionArgument): boolean¶
Returns true if and only if we have syntactic proof that the /g flag is absent. Returns false in all other cases (i.e. it still might or might not contain the global flag).
Raw JSDoc
Calls:
node.arguments.atgetStaticValue (from ../util)flagsValue.value.includes
Code
function definitelyDoesNotContainGlobalFlag(
node: TSESTree.CallExpressionArgument,
): boolean {
if (
(node.type === AST_NODE_TYPES.CallExpression ||
node.type === AST_NODE_TYPES.NewExpression) &&
node.callee.type === AST_NODE_TYPES.Identifier &&
node.callee.name === 'RegExp'
) {
const flags = node.arguments.at(1);
if (!flags) {
return true;
}
const flagsValue = getStaticValue(flags, globalScope);
return (
!!flagsValue &&
(typeof flagsValue.value !== 'string' ||
!flagsValue.value.includes('g'))
);
}
return false;
}
Enums¶
enum ArgumentType¶
Enum Code
Members¶
| Name | Value | Description |
|---|---|---|
Other |
0 |
not shown |
String |
1 << 0 |
not shown |
RegExp |
1 << 1 |
not shown |
Both |
String \| RegExp |
not shown |
Generated by Syntax Scribe