Skip to content

Commit e7d1bb0

Browse files
committed
Extract code from no-array-reverse
1 parent 1601e9c commit e7d1bb0

File tree

2 files changed

+131
-108
lines changed

2 files changed

+131
-108
lines changed

rules/no-array-reverse.js

Lines changed: 2 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,3 @@
1-
import {isMethodCall} from './ast/index.js';
2-
import {getParenthesizedText} from './utils/index.js';
1+
import noArrayMutateRule from './shared/no-array-mutate-rule.js';
32

4-
const MESSAGE_ID_ERROR = 'no-array-reverse/error';
5-
const MESSAGE_ID_SUGGESTION_ONLY_FIX_METHOD = 'no-array-reverse/suggestion-only-fix-method';
6-
const MESSAGE_ID_SUGGESTION_SPREADING_ARRAY = 'no-array-reverse/suggestion-spreading-array';
7-
const MESSAGE_ID_SUGGESTION_NOT_SPREADING_ARRAY = 'no-array-reverse/suggestion-not-spreading-array';
8-
const messages = {
9-
[MESSAGE_ID_ERROR]: 'Use `Array#toReversed()` instead of `Array#reverse()`.',
10-
[MESSAGE_ID_SUGGESTION_ONLY_FIX_METHOD]: 'Switch to `.toReversed()`.',
11-
[MESSAGE_ID_SUGGESTION_SPREADING_ARRAY]: 'The spreading object is an array',
12-
[MESSAGE_ID_SUGGESTION_NOT_SPREADING_ARRAY]: 'The spreading object is NOT an array',
13-
};
14-
15-
/** @param {import('eslint').Rule.RuleContext} context */
16-
const create = context => {
17-
const {sourceCode} = context;
18-
const {allowExpressionStatement} = context.options[0];
19-
20-
return {
21-
CallExpression(callExpression) {
22-
if (!isMethodCall(callExpression, {
23-
method: 'reverse',
24-
argumentsLength: 0,
25-
optionalCall: false,
26-
})) {
27-
return;
28-
}
29-
30-
const array = callExpression.callee.object;
31-
32-
// `[...array].reverse()`
33-
const isSpreadAndReverse = array.type === 'ArrayExpression'
34-
&& array.elements.length === 1
35-
&& array.elements[0].type === 'SpreadElement';
36-
37-
if (allowExpressionStatement && !isSpreadAndReverse) {
38-
const maybeExpressionStatement = callExpression.parent.type === 'ChainExpression'
39-
? callExpression.parent.parent
40-
: callExpression.parent;
41-
if (maybeExpressionStatement.type === 'ExpressionStatement') {
42-
return;
43-
}
44-
}
45-
46-
const reverseProperty = callExpression.callee.property;
47-
const suggestions = [];
48-
const fixMethodName = fixer => fixer.replaceText(reverseProperty, 'toReversed');
49-
50-
/*
51-
For `[...array].reverse()`, provide two suggestion, let user choose if the object can be unwrapped,
52-
otherwise only change `.reverse()` to `.toReversed()`
53-
*/
54-
if (isSpreadAndReverse) {
55-
suggestions.push({
56-
messageId: MESSAGE_ID_SUGGESTION_SPREADING_ARRAY,
57-
* fix(fixer) {
58-
const text = getParenthesizedText(array.elements[0].argument, sourceCode);
59-
yield fixer.replaceText(array, text);
60-
yield fixMethodName(fixer);
61-
},
62-
});
63-
}
64-
65-
suggestions.push({
66-
messageId: isSpreadAndReverse
67-
? MESSAGE_ID_SUGGESTION_NOT_SPREADING_ARRAY
68-
: MESSAGE_ID_SUGGESTION_ONLY_FIX_METHOD,
69-
fix: fixMethodName,
70-
});
71-
72-
return {
73-
node: reverseProperty,
74-
messageId: MESSAGE_ID_ERROR,
75-
suggest: suggestions,
76-
};
77-
},
78-
};
79-
};
80-
81-
const schema = [
82-
{
83-
type: 'object',
84-
additionalProperties: false,
85-
properties: {
86-
allowExpressionStatement: {
87-
type: 'boolean',
88-
},
89-
},
90-
},
91-
];
92-
93-
/** @type {import('eslint').Rule.RuleModule} */
94-
const config = {
95-
create,
96-
meta: {
97-
type: 'suggestion',
98-
docs: {
99-
description: 'Prefer `Array#toReversed()` over `Array#reverse()`.',
100-
recommended: true,
101-
},
102-
hasSuggestions: true,
103-
schema,
104-
defaultOptions: [{allowExpressionStatement: true}],
105-
messages,
106-
},
107-
};
108-
109-
export default config;
3+
export default noArrayMutateRule('reverse');

rules/shared/no-array-mutate-rule.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import {isMethodCall} from '../ast/index.js';
2+
import {getParenthesizedText} from '../utils/index.js';
3+
4+
const schema = [
5+
{
6+
type: 'object',
7+
additionalProperties: false,
8+
properties: {
9+
allowExpressionStatement: {
10+
type: 'boolean',
11+
},
12+
},
13+
},
14+
];
15+
16+
const MESSAGE_ID_ERROR = 'error';
17+
const MESSAGE_ID_SUGGESTION_APPLY_REPLACEMENT = 'suggestion-apply-replacement';
18+
const MESSAGE_ID_SUGGESTION_SPREADING_ARRAY = 'suggestion-spreading-array';
19+
const MESSAGE_ID_SUGGESTION_NOT_SPREADING_ARRAY = 'suggestion-not-spreading-array';
20+
21+
const methods = new Map([
22+
[
23+
'reverse',
24+
{
25+
replacement: 'toReversed',
26+
predicate: callExpression => isMethodCall(callExpression, {
27+
method: 'reverse',
28+
argumentsLength: 0,
29+
optionalCall: false,
30+
}),
31+
},
32+
],
33+
]);
34+
35+
function noArrayMutateRule(methodName) {
36+
const {
37+
replacement,
38+
predicate,
39+
} = methods.get(methodName);
40+
41+
const messages = {
42+
[MESSAGE_ID_ERROR]: `Use \`Array#${replacement}()\` instead of \`Array#${methodName}()\`.`,
43+
[MESSAGE_ID_SUGGESTION_APPLY_REPLACEMENT]: `Switch to \`.${replacement}()\`.`,
44+
[MESSAGE_ID_SUGGESTION_SPREADING_ARRAY]: 'The spreading object is an array',
45+
[MESSAGE_ID_SUGGESTION_NOT_SPREADING_ARRAY]: 'The spreading object is NOT an array',
46+
};
47+
48+
/** @param {import('eslint').Rule.RuleContext} context */
49+
const create = context => {
50+
const {sourceCode} = context;
51+
const {allowExpressionStatement} = context.options[0];
52+
53+
return {
54+
CallExpression(callExpression) {
55+
if (!predicate(callExpression)) {
56+
return;
57+
}
58+
59+
const array = callExpression.callee.object;
60+
61+
// `[...array].reverse()`
62+
const isSpreadAndMutate = array.type === 'ArrayExpression'
63+
&& array.elements.length === 1
64+
&& array.elements[0].type === 'SpreadElement';
65+
66+
if (allowExpressionStatement && !isSpreadAndMutate) {
67+
const maybeExpressionStatement = callExpression.parent.type === 'ChainExpression'
68+
? callExpression.parent.parent
69+
: callExpression.parent;
70+
if (maybeExpressionStatement.type === 'ExpressionStatement') {
71+
return;
72+
}
73+
}
74+
75+
const methodProperty = callExpression.callee.property;
76+
const suggestions = [];
77+
const fixMethodName = fixer => fixer.replaceText(methodProperty, replacement);
78+
79+
/*
80+
For `[...array].reverse()`, provide two suggestions, let user choose if the object can be unwrapped,
81+
otherwise only change `.reverse()` to `.toReversed()`
82+
*/
83+
if (isSpreadAndMutate) {
84+
suggestions.push({
85+
messageId: MESSAGE_ID_SUGGESTION_SPREADING_ARRAY,
86+
* fix(fixer) {
87+
const text = getParenthesizedText(array.elements[0].argument, sourceCode);
88+
yield fixer.replaceText(array, text);
89+
yield fixMethodName(fixer);
90+
},
91+
});
92+
}
93+
94+
suggestions.push({
95+
messageId: isSpreadAndMutate
96+
? MESSAGE_ID_SUGGESTION_NOT_SPREADING_ARRAY
97+
: MESSAGE_ID_SUGGESTION_APPLY_REPLACEMENT,
98+
fix: fixMethodName,
99+
});
100+
101+
return {
102+
node: methodProperty,
103+
messageId: MESSAGE_ID_ERROR,
104+
suggest: suggestions,
105+
};
106+
},
107+
};
108+
};
109+
110+
/** @type {import('eslint').Rule.RuleModule} */
111+
const config = {
112+
create,
113+
meta: {
114+
type: 'suggestion',
115+
docs: {
116+
description: `Prefer \`Array#${replacement}()\` over \`Array#${methodName}()\`.`,
117+
recommended: true,
118+
},
119+
hasSuggestions: true,
120+
schema,
121+
defaultOptions: [{allowExpressionStatement: true}],
122+
messages,
123+
},
124+
};
125+
126+
return config;
127+
}
128+
129+
export default noArrayMutateRule;

0 commit comments

Comments
 (0)