Skip to content

Commit f9ed3e1

Browse files
authored
Refactor to use simple selectors (#2116)
1 parent 9a6012c commit f9ed3e1

22 files changed

+252
-205
lines changed

rules/ast/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ module.exports = {
2626
isCallExpression,
2727
isCallOrNewExpression,
2828
isEmptyNode: require('./is-empty-node.js'),
29+
isFunction: require('./is-function.js'),
2930
isMemberExpression: require('./is-member-expression.js'),
3031
isMethodCall: require('./is-method-call.js'),
3132
isNewExpression,

rules/ast/is-function.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
'use strict';
2+
3+
function isFunction(node) {
4+
return node.type === 'FunctionDeclaration'
5+
|| node.type === 'FunctionExpression'
6+
|| node.type === 'ArrowFunctionExpression';
7+
}
8+
9+
module.exports = isFunction;

rules/consistent-destructuring.js

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,11 @@
11
'use strict';
22
const avoidCapture = require('./utils/avoid-capture.js');
3-
const {not} = require('./selectors/index.js');
43
const isLeftHandSide = require('./utils/is-left-hand-side.js');
4+
const {isCallOrNewExpression} = require('./ast/index.js');
55

66
const MESSAGE_ID = 'consistentDestructuring';
77
const MESSAGE_ID_SUGGEST = 'consistentDestructuringSuggest';
88

9-
const declaratorSelector = [
10-
'VariableDeclarator',
11-
'[id.type="ObjectPattern"]',
12-
'[init]',
13-
'[init.type!="Literal"]',
14-
].join('');
15-
16-
const memberSelector = [
17-
'MemberExpression',
18-
'[computed!=true]',
19-
not([
20-
'CallExpression > .callee',
21-
'NewExpression> .callee',
22-
]),
23-
].join('');
24-
259
const isSimpleExpression = expression => {
2610
while (expression) {
2711
if (expression.computed) {
@@ -57,9 +41,14 @@ const create = context => {
5741
const declarations = new Map();
5842

5943
return {
60-
[declaratorSelector](node) {
61-
// Ignore any complex expressions (e.g. arrays, functions)
62-
if (!isSimpleExpression(node.init)) {
44+
VariableDeclarator(node) {
45+
if (!(
46+
node.id.type === 'ObjectPattern'
47+
&& node.init
48+
&& node.init.type !== 'Literal'
49+
// Ignore any complex expressions (e.g. arrays, functions)
50+
&& isSimpleExpression(node.init)
51+
)) {
6352
return;
6453
}
6554

@@ -69,8 +58,15 @@ const create = context => {
6958
objectPattern: node.id,
7059
});
7160
},
72-
[memberSelector](node) {
73-
if (isLeftHandSide(node)) {
61+
MemberExpression(node) {
62+
if (
63+
node.computed
64+
|| (
65+
isCallOrNewExpression(node.parent)
66+
&& node.parent.callee === node
67+
)
68+
|| isLeftHandSide(node)
69+
) {
7470
return;
7571
}
7672

rules/escape-case.js

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict';
22
const {replaceTemplateElement} = require('./fix/index.js');
3+
const {isRegexLiteral, isStringLiteral} = require('./ast/index.js');
34

45
const MESSAGE_ID = 'escape-case';
56
const messages = {
@@ -23,21 +24,20 @@ const getProblem = ({node, original, regex = escapeWithLowercase, fix}) => {
2324
/** @param {import('eslint').Rule.RuleContext} context */
2425
const create = () => ({
2526
Literal(node) {
26-
if (typeof node.value !== 'string') {
27-
return;
27+
if (isStringLiteral(node)) {
28+
return getProblem({
29+
node,
30+
original: node.raw,
31+
});
2832
}
2933

30-
return getProblem({
31-
node,
32-
original: node.raw,
33-
});
34-
},
35-
'Literal[regex]'(node) {
36-
return getProblem({
37-
node,
38-
original: node.raw,
39-
regex: escapePatternWithLowercase,
40-
});
34+
if (isRegexLiteral(node)) {
35+
return getProblem({
36+
node,
37+
original: node.raw,
38+
regex: escapePatternWithLowercase,
39+
});
40+
}
4141
},
4242
TemplateElement(node) {
4343
return getProblem({

rules/no-await-expression-member.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ const create = context => {
1515
const {sourceCode} = context;
1616

1717
return {
18-
'MemberExpression[object.type="AwaitExpression"]'(memberExpression) {
18+
MemberExpression(memberExpression) {
19+
if (memberExpression.object.type !== 'AwaitExpression') {
20+
return;
21+
}
22+
1923
const {property} = memberExpression;
2024
const problem = {
2125
node: property,

rules/no-hex-escape.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict';
22
const {replaceTemplateElement} = require('./fix/index.js');
3+
const {isStringLiteral, isRegexLiteral} = require('./ast/index.js');
34

45
const MESSAGE_ID = 'no-hex-escape';
56
const messages = {
@@ -24,7 +25,7 @@ function checkEscape(context, node, value) {
2425
/** @param {import('eslint').Rule.RuleContext} context */
2526
const create = context => ({
2627
Literal(node) {
27-
if (node.regex || typeof node.value === 'string') {
28+
if (isStringLiteral(node) || isRegexLiteral(node)) {
2829
return checkEscape(context, node, node.raw);
2930
}
3031
},

rules/no-instanceof-array.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,21 @@ const MESSAGE_ID = 'no-instanceof-array';
99
const messages = {
1010
[MESSAGE_ID]: 'Use `Array.isArray()` instead of `instanceof Array`.',
1111
};
12-
const selector = [
13-
'BinaryExpression',
14-
'[operator="instanceof"]',
15-
'[right.type="Identifier"]',
16-
'[right.name="Array"]',
17-
].join('');
1812

1913
/** @param {import('eslint').Rule.RuleContext} context */
2014
const create = context => {
2115
const {sourceCode} = context;
2216

2317
return {
24-
[selector](node) {
18+
BinaryExpression(node) {
19+
if (!(
20+
node.operator === 'instanceof'
21+
&& node.right.type === 'Identifier'
22+
&& node.right.name === 'Array'
23+
)) {
24+
return;
25+
}
26+
2527
const {left, right} = node;
2628
let tokenStore = sourceCode;
2729
let instanceofToken = tokenStore.getTokenAfter(left, isInstanceofToken);

rules/no-object-as-default-parameter.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
'use strict';
2+
const {isFunction} = require('./ast/index.js');
23

34
const MESSAGE_ID_IDENTIFIER = 'identifier';
45
const MESSAGE_ID_NON_IDENTIFIER = 'non-identifier';
@@ -7,15 +8,18 @@ const messages = {
78
[MESSAGE_ID_NON_IDENTIFIER]: 'Do not use an object literal as default.',
89
};
910

10-
const objectParameterSelector = [
11-
':function > AssignmentPattern.params',
12-
'[right.type="ObjectExpression"]',
13-
'[right.properties.length>0]',
14-
].join('');
15-
1611
/** @param {import('eslint').Rule.RuleContext} context */
1712
const create = () => ({
18-
[objectParameterSelector](node) {
13+
AssignmentPattern(node) {
14+
if (!(
15+
node.right.type === 'ObjectExpression'
16+
&& node.right.properties.length > 0
17+
&& isFunction(node.parent)
18+
&& node.parent.params.includes(node)
19+
)) {
20+
return;
21+
}
22+
1923
const {left, right} = node;
2024

2125
if (left.type === 'Identifier') {

rules/no-static-only-class.js

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,6 @@ const messages = {
99
[MESSAGE_ID]: 'Use an object instead of a class with only static members.',
1010
};
1111

12-
const selector = [
13-
':matches(ClassDeclaration, ClassExpression)',
14-
':not([superClass], [decorators.length>0])',
15-
'[body.type="ClassBody"]',
16-
'[body.body.length>0]',
17-
].join('');
18-
1912
const isEqualToken = ({type, value}) => type === 'Punctuator' && value === '=';
2013
const isDeclarationOfExportDefaultDeclaration = node =>
2114
node.type === 'ClassDeclaration'
@@ -197,14 +190,20 @@ function switchClassToObject(node, sourceCode) {
197190
}
198191

199192
function create(context) {
200-
const {sourceCode} = context;
201-
202193
return {
203-
[selector](node) {
204-
if (node.body.body.some(node => !isStaticMember(node))) {
194+
'ClassDeclaration,ClassExpression'(node) {
195+
if (
196+
node.superClass
197+
|| (node.decorators && node.decorators.length > 0)
198+
|| node.body.type !== 'ClassBody'
199+
|| node.body.body.length === 0
200+
|| node.body.body.some(node => !isStaticMember(node))
201+
) {
205202
return;
206203
}
207204

205+
const {sourceCode} = context;
206+
208207
return {
209208
node,
210209
loc: getClassHeadLocation(node, sourceCode),

rules/no-this-assignment.js

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,25 @@
11
'use strict';
2-
const {matches} = require('./selectors/index.js');
3-
42
const MESSAGE_ID = 'no-this-assignment';
53
const messages = {
64
[MESSAGE_ID]: 'Do not assign `this` to `{{name}}`.',
75
};
86

9-
const variableDeclaratorSelector = [
10-
'VariableDeclarator',
11-
'[init.type="ThisExpression"]',
12-
'[id.type="Identifier"]',
13-
].join('');
14-
15-
const assignmentExpressionSelector = [
16-
'AssignmentExpression',
17-
'[right.type="ThisExpression"]',
18-
'[left.type="Identifier"]',
19-
].join('');
20-
21-
const selector = matches([variableDeclaratorSelector, assignmentExpressionSelector]);
22-
237
/** @param {import('eslint').Rule.RuleContext} context */
248
const create = () => ({
25-
[selector](node) {
26-
const variable = node.type === 'AssignmentExpression' ? node.left : node.id;
9+
'VariableDeclarator,AssignmentExpression'(node) {
10+
const variableNode = node.type === 'AssignmentExpression' ? node.left : node.id;
11+
const valueNode = node.type === 'AssignmentExpression' ? node.right : node.init;
12+
13+
if (
14+
variableNode.type !== 'Identifier'
15+
|| valueNode?.type !== 'ThisExpression'
16+
) {
17+
return;
18+
}
19+
2720
return {
2821
node,
29-
data: {name: variable.name},
22+
data: {name: variableNode.name},
3023
messageId: MESSAGE_ID,
3124
};
3225
},

0 commit comments

Comments
 (0)