/
prefer-regexp-exec.ts
66 lines (59 loc) · 1.85 KB
/
prefer-regexp-exec.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import { TSESTree } from '@typescript-eslint/typescript-estree';
import { createRule, getParserServices, getTypeName } from '../util';
import { getStaticValue } from 'eslint-utils';
export default createRule({
name: 'prefer-regexp-exec',
defaultOptions: [],
meta: {
type: 'suggestion',
docs: {
description:
'Prefer RegExp#exec() over String#match() if no global flag is provided.',
category: 'Best Practices',
recommended: false,
},
messages: {
regExpExecOverStringMatch: 'Use the `RegExp#exec()` method instead.',
},
schema: [],
},
create(context) {
const globalScope = context.getScope();
const service = getParserServices(context);
const typeChecker = service.program.getTypeChecker();
/**
* Check if a given node is a string.
* @param node The node to check.
*/
function isStringType(node: TSESTree.Node): boolean {
const objectType = typeChecker.getTypeAtLocation(
service.esTreeNodeToTSNodeMap.get(node),
);
return getTypeName(typeChecker, objectType) === 'string';
}
return {
"CallExpression[arguments.length=1] > MemberExpression.callee[property.name='match'][computed=false]"(
node: TSESTree.MemberExpression,
) {
const callNode = node.parent as TSESTree.CallExpression;
const arg = callNode.arguments[0];
const evaluated = getStaticValue(arg, globalScope);
// Don't report regular expressions with global flag.
if (
evaluated &&
evaluated.value instanceof RegExp &&
evaluated.value.flags.includes('g')
) {
return;
}
if (isStringType(node.object)) {
context.report({
node: callNode,
messageId: 'regExpExecOverStringMatch',
});
return;
}
},
};
},
});