eslint-plugin-unicorn 69.0.0 → 71.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +26 -25
- package/readme.md +15 -1
- package/rules/ast/call-or-new-expression.js +2 -8
- package/rules/ast/is-member-expression.js +2 -8
- package/rules/comment-content.js +4 -4
- package/rules/consistent-boolean-name.js +727 -42
- package/rules/consistent-compound-words.js +1 -2
- package/rules/consistent-json-file-read.js +2 -5
- package/rules/dom-node-dataset.js +3 -7
- package/rules/escape-case.js +4 -4
- package/rules/expiring-todo-comments.js +25 -2
- package/rules/explicit-length-check.js +3 -3
- package/rules/filename-case.js +4 -4
- package/rules/fix/{add-parenthesizes-to-return-or-throw-expression.js → add-parentheses-to-return-or-throw-expression.js} +1 -1
- package/rules/fix/index.js +1 -1
- package/rules/fix/remove-argument.js +34 -8
- package/rules/fix/remove-statement.js +1 -3
- package/rules/fix/switch-new-expression-to-call-expression.js +2 -2
- package/rules/id-match.js +2 -2
- package/rules/index.js +10 -0
- package/rules/isolated-functions.js +203 -19
- package/rules/name-replacements.js +1 -2
- package/rules/no-anonymous-default-export.js +1 -3
- package/rules/no-array-callback-reference.js +2 -10
- package/rules/no-async-promise-finally.js +111 -0
- package/rules/no-chained-comparison.js +1 -1
- package/rules/no-collection-bracket-access.js +27 -4
- package/rules/no-computed-property-existence-check.js +1 -4
- package/rules/no-declarations-before-early-exit.js +6 -5
- package/rules/no-error-property-assignment.js +3 -3
- package/rules/no-for-loop.js +4 -4
- package/rules/no-keyword-prefix.js +2 -2
- package/rules/no-manually-wrapped-comments.js +5 -1
- package/rules/no-mismatched-map-key.js +13 -20
- package/rules/no-named-default.js +2 -5
- package/rules/no-negated-array-predicate.js +6 -13
- package/rules/no-negated-comparison.js +31 -71
- package/rules/no-negated-condition.js +2 -2
- package/rules/no-negation-in-equality-check.js +4 -7
- package/rules/no-non-function-verb-prefix.js +25 -2
- package/rules/no-process-exit.js +6 -6
- package/rules/no-return-array-push.js +9 -2
- package/rules/no-static-only-class.js +2 -8
- package/rules/no-thenable.js +9 -15
- package/rules/no-typeof-undefined.js +2 -2
- package/rules/no-undeclared-class-members.js +1 -5
- package/rules/no-unnecessary-array-flat-map.js +308 -0
- package/rules/no-unnecessary-await.js +2 -2
- package/rules/no-unnecessary-fetch-options.js +624 -0
- package/rules/no-unnecessary-global-this.js +5 -9
- package/rules/no-unsafe-promise-all-settled-values.js +865 -0
- package/rules/no-unused-properties.js +0 -1
- package/rules/no-useless-coercion.js +1 -2
- package/rules/no-useless-else.js +32 -1
- package/rules/no-useless-iterator-to-array.js +42 -16
- package/rules/no-useless-spread.js +2 -2
- package/rules/no-useless-undefined.js +2 -2
- package/rules/prefer-abort-signal-any.js +1391 -0
- package/rules/prefer-array-find.js +2 -5
- package/rules/prefer-array-flat-map.js +6 -11
- package/rules/prefer-array-from-range.js +98 -0
- package/rules/prefer-bigint-literals.js +1 -0
- package/rules/prefer-block-statement-over-iife.js +159 -0
- package/rules/prefer-boolean-return.js +20 -3
- package/rules/prefer-continue.js +6 -2
- package/rules/prefer-default-parameters.js +2 -2
- package/rules/prefer-early-return.js +3 -1
- package/rules/prefer-else-if.js +5 -9
- package/rules/prefer-group-by.js +576 -0
- package/rules/prefer-has-check.js +6 -3
- package/rules/prefer-https.js +3 -3
- package/rules/prefer-identifier-import-export-specifiers.js +1 -3
- package/rules/prefer-iterator-helpers.js +196 -0
- package/rules/prefer-minimal-ternary.js +4 -4
- package/rules/prefer-number-coercion.js +1 -1
- package/rules/prefer-object-define-properties.js +1 -3
- package/rules/prefer-object-iterable-methods.js +57 -14
- package/rules/prefer-observer-apis.js +2 -5
- package/rules/prefer-private-class-fields.js +1 -3
- package/rules/prefer-simple-sort-comparator.js +6 -6
- package/rules/prefer-simplified-conditions.js +584 -0
- package/rules/prefer-single-object-destructuring.js +2 -5
- package/rules/prefer-single-replace.js +2 -2
- package/rules/prefer-spread.js +25 -32
- package/rules/prefer-string-pad-start-end.js +1 -6
- package/rules/prefer-string-raw.js +2 -2
- package/rules/prefer-string-repeat.js +16 -9
- package/rules/prefer-string-replace-all.js +1 -1
- package/rules/prefer-ternary.js +5 -5
- package/rules/prefer-toggle-attribute.js +3 -3
- package/rules/prefer-top-level-await.js +6 -1
- package/rules/prefer-uint8array-base64.js +3 -1
- package/rules/require-proxy-trap-boolean-return.js +2 -2
- package/rules/rule/to-eslint-create.js +1 -2
- package/rules/rule/to-eslint-listener.js +1 -3
- package/rules/rule/to-eslint-problem.js +1 -3
- package/rules/rule/to-eslint-rule-fixer.js +1 -3
- package/rules/rule/to-eslint-rule.js +1 -3
- package/rules/rule/to-eslint-rules.js +1 -3
- package/rules/rule/unicorn-context.js +1 -3
- package/rules/rule/unicorn-listeners.js +1 -3
- package/rules/shared/array-range.js +66 -0
- package/rules/shared/iterator-helpers.js +43 -1
- package/rules/shared/no-array-mutate-rule.js +1 -3
- package/rules/shared/regexp-escape.js +4 -4
- package/rules/utils/can-tokens-be-adjacent.js +95 -0
- package/rules/utils/comparison.js +65 -0
- package/rules/utils/escape-string.js +2 -8
- package/rules/utils/get-available-variable-name.js +2 -13
- package/rules/utils/get-precedence.js +112 -0
- package/rules/utils/get-token-store.js +1 -3
- package/rules/utils/global-reference-tracker.js +3 -6
- package/rules/utils/index.js +4 -0
- package/rules/utils/is-boolean.js +103 -30
- package/rules/utils/is-identifier-name.js +5 -0
- package/rules/utils/is-promise-type.js +1 -3
- package/rules/utils/is-react-hook-name.js +5 -0
- package/rules/utils/parentheses/get-parent-syntax-opening-parenthesis.js +1 -3
- package/rules/utils/parentheses/iterate-surrounding-parentheses.js +1 -1
- package/rules/utils/should-add-parentheses-to-await-expression-argument.js +4 -9
- package/rules/utils/should-add-parentheses-to-call-expression-callee.js +6 -12
- package/rules/utils/should-add-parentheses-to-unary-expression.js +7 -14
- package/rules/utils/to-location.js +1 -3
- package/rules/utils/track-branch-exits.js +8 -7
|
@@ -179,11 +179,7 @@ const getDeclaredClassMemberNames = (classBody, sourceCode) => {
|
|
|
179
179
|
const names = new Set(['constructor']);
|
|
180
180
|
|
|
181
181
|
for (const member of classBody.body) {
|
|
182
|
-
if (!(
|
|
183
|
-
classMemberTypes.has(member.type)
|
|
184
|
-
&& member.static !== true
|
|
185
|
-
&& member.kind !== 'constructor'
|
|
186
|
-
)) {
|
|
182
|
+
if (!classMemberTypes.has(member.type) || member.static === true || member.kind === 'constructor') {
|
|
187
183
|
continue;
|
|
188
184
|
}
|
|
189
185
|
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import {hasSideEffect} from '@eslint-community/eslint-utils';
|
|
2
|
+
import {isEmptyArrayExpression, isMethodCall} from './ast/index.js';
|
|
3
|
+
import {
|
|
4
|
+
getParenthesizedText,
|
|
5
|
+
isKnownNonArray,
|
|
6
|
+
isParenthesized,
|
|
7
|
+
isSameIdentifier,
|
|
8
|
+
isTypeScriptFile,
|
|
9
|
+
shouldAddParenthesesToMemberExpressionObject,
|
|
10
|
+
wouldRemoveComments,
|
|
11
|
+
} from './utils/index.js';
|
|
12
|
+
|
|
13
|
+
const MESSAGE_ID = 'no-unnecessary-array-flat-map';
|
|
14
|
+
const SUGGESTION_ID_FILTER_MAP = 'no-unnecessary-array-flat-map/filter-map-suggestion';
|
|
15
|
+
|
|
16
|
+
const messages = {
|
|
17
|
+
[MESSAGE_ID]: 'Prefer `.{{method}}(…)` over `.flatMap(…)` for this single-item array callback.',
|
|
18
|
+
[SUGGESTION_ID_FILTER_MAP]: 'Replace `.flatMap(…)` with `.filter(…).map(…)`.',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const arrowBodyNeedsParenthesesTypes = new Set([
|
|
22
|
+
'ObjectExpression',
|
|
23
|
+
'SequenceExpression',
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
const typescriptExpressionWrapperTypes = new Set([
|
|
27
|
+
'TSAsExpression',
|
|
28
|
+
'TSNonNullExpression',
|
|
29
|
+
'TSSatisfiesExpression',
|
|
30
|
+
'TSTypeAssertion',
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
const hasTypeArguments = node => node.typeArguments || node.typeParameters;
|
|
34
|
+
|
|
35
|
+
const isFilterCallExpression = node => isMethodCall(node, {
|
|
36
|
+
method: 'filter',
|
|
37
|
+
argumentsLength: 1,
|
|
38
|
+
optionalCall: false,
|
|
39
|
+
optionalMember: false,
|
|
40
|
+
computed: false,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const isSimpleSingleParameterArrowCallback = node =>
|
|
44
|
+
node.type === 'ArrowFunctionExpression'
|
|
45
|
+
&& !node.async
|
|
46
|
+
&& !node.returnType
|
|
47
|
+
&& !node.typeParameters
|
|
48
|
+
&& node.params.length === 1
|
|
49
|
+
&& node.params[0].type === 'Identifier'
|
|
50
|
+
&& !node.params[0].optional
|
|
51
|
+
&& node.body.type !== 'BlockStatement';
|
|
52
|
+
|
|
53
|
+
const getSingleArrayElement = node => {
|
|
54
|
+
if (
|
|
55
|
+
node.type !== 'ArrayExpression'
|
|
56
|
+
|| node.elements.length !== 1
|
|
57
|
+
|| !node.elements[0]
|
|
58
|
+
|| node.elements[0].type === 'SpreadElement'
|
|
59
|
+
) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return node.elements[0];
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
function shouldParenthesizeArrowBody(node, context) {
|
|
67
|
+
if (isParenthesized(node, context)) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (arrowBodyNeedsParenthesesTypes.has(node.type)) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return typescriptExpressionWrapperTypes.has(node.type) && shouldParenthesizeArrowBody(node.expression, context);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getCallbackResult(callback) {
|
|
79
|
+
const directElement = getSingleArrayElement(callback.body);
|
|
80
|
+
if (directElement) {
|
|
81
|
+
return {
|
|
82
|
+
type: 'map',
|
|
83
|
+
element: directElement,
|
|
84
|
+
arrayExpression: callback.body,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (callback.body.type !== 'ConditionalExpression') {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const {test, consequent, alternate} = callback.body;
|
|
93
|
+
const element = getSingleArrayElement(consequent);
|
|
94
|
+
if (!element || !isEmptyArrayExpression(alternate)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
type: 'conditional',
|
|
100
|
+
test,
|
|
101
|
+
element,
|
|
102
|
+
arrayExpression: consequent,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function getArrowBodyText(node, context) {
|
|
107
|
+
if (isParenthesized(node, context)) {
|
|
108
|
+
return getParenthesizedText(node, context);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const text = context.sourceCode.getText(node);
|
|
112
|
+
return shouldParenthesizeArrowBody(node, context)
|
|
113
|
+
? `(${text})`
|
|
114
|
+
: text;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function getMemberExpressionObjectText(node, context) {
|
|
118
|
+
if (node.type === 'Super') {
|
|
119
|
+
return 'super';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (isParenthesized(node, context)) {
|
|
123
|
+
return getParenthesizedText(node, context);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const text = context.sourceCode.getText(node);
|
|
127
|
+
return shouldAddParenthesesToMemberExpressionObject(node, context) ? `(${text})` : text;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function getArrowParameterText(callback, context) {
|
|
131
|
+
const parameterText = context.sourceCode.getText(callback.params[0]);
|
|
132
|
+
return callback.params[0].typeAnnotation ? `(${parameterText})` : parameterText;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getFilterMapSuggestion(flatMapCallExpression, callback, callbackResult, context) {
|
|
136
|
+
if (
|
|
137
|
+
hasTypeArguments(flatMapCallExpression)
|
|
138
|
+
|| callback.params[0].typeAnnotation
|
|
139
|
+
|| wouldRemoveComments(context, flatMapCallExpression, [
|
|
140
|
+
flatMapCallExpression.callee.object,
|
|
141
|
+
callbackResult.test,
|
|
142
|
+
callbackResult.element,
|
|
143
|
+
])
|
|
144
|
+
|| hasSideEffect(callbackResult.test, context.sourceCode)
|
|
145
|
+
|| hasSideEffect(callbackResult.element, context.sourceCode)
|
|
146
|
+
) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const arrayText = getMemberExpressionObjectText(flatMapCallExpression.callee.object, context);
|
|
151
|
+
const parameterText = getArrowParameterText(callback, context);
|
|
152
|
+
const testText = getArrowBodyText(callbackResult.test, context);
|
|
153
|
+
const elementText = getArrowBodyText(callbackResult.element, context);
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
messageId: SUGGESTION_ID_FILTER_MAP,
|
|
157
|
+
fix: fixer => fixer.replaceText(
|
|
158
|
+
flatMapCallExpression,
|
|
159
|
+
`${arrayText}.filter(${parameterText} => ${testText}).map(${parameterText} => ${elementText})`,
|
|
160
|
+
),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function getProblemForFilterFlatMap(flatMapCallExpression, callbackResult, context) {
|
|
165
|
+
const filterCallExpression = flatMapCallExpression.callee.object;
|
|
166
|
+
if (!isFilterCallExpression(filterCallExpression)) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const problem = {
|
|
171
|
+
node: flatMapCallExpression.callee.property,
|
|
172
|
+
messageId: MESSAGE_ID,
|
|
173
|
+
data: {method: 'map'},
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
if (
|
|
177
|
+
hasTypeArguments(filterCallExpression)
|
|
178
|
+
|| hasTypeArguments(flatMapCallExpression)
|
|
179
|
+
|| isKnownNonArray(filterCallExpression.callee.object, context)
|
|
180
|
+
|| wouldRemoveComments(context, callbackResult.arrayExpression, [callbackResult.element])
|
|
181
|
+
) {
|
|
182
|
+
return problem;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
...problem,
|
|
187
|
+
* fix(fixer) {
|
|
188
|
+
yield fixer.replaceText(flatMapCallExpression.callee.property, 'map');
|
|
189
|
+
yield fixer.replaceText(callbackResult.arrayExpression, getArrowBodyText(callbackResult.element, context));
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function getProblemForConditionalFlatMap(flatMapCallExpression, callback, callbackResult, context) {
|
|
195
|
+
if (isTypeScriptFile(context.physicalFilename)) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const method = isSameIdentifier(callbackResult.element, callback.params[0]) ? 'filter' : 'filter().map';
|
|
200
|
+
const problem = {
|
|
201
|
+
node: flatMapCallExpression.callee.property,
|
|
202
|
+
messageId: MESSAGE_ID,
|
|
203
|
+
data: {method},
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
if (method === 'filter') {
|
|
207
|
+
if (
|
|
208
|
+
hasTypeArguments(flatMapCallExpression)
|
|
209
|
+
|| hasSideEffect(callbackResult.test, context.sourceCode)
|
|
210
|
+
|| wouldRemoveComments(context, flatMapCallExpression, [
|
|
211
|
+
flatMapCallExpression.callee.object,
|
|
212
|
+
callback.params[0],
|
|
213
|
+
callbackResult.test,
|
|
214
|
+
])
|
|
215
|
+
) {
|
|
216
|
+
return problem;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const arrayText = getMemberExpressionObjectText(flatMapCallExpression.callee.object, context);
|
|
220
|
+
const parameterText = getArrowParameterText(callback, context);
|
|
221
|
+
const testText = getArrowBodyText(callbackResult.test, context);
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
...problem,
|
|
225
|
+
fix: fixer => fixer.replaceText(
|
|
226
|
+
flatMapCallExpression,
|
|
227
|
+
`${arrayText}.filter(${parameterText} => ${testText})`,
|
|
228
|
+
),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const suggestion = getFilterMapSuggestion(flatMapCallExpression, callback, callbackResult, context);
|
|
233
|
+
return suggestion
|
|
234
|
+
? {
|
|
235
|
+
...problem,
|
|
236
|
+
suggest: [suggestion],
|
|
237
|
+
}
|
|
238
|
+
: problem;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function getProblem(flatMapCallExpression, context) {
|
|
242
|
+
if (
|
|
243
|
+
!isMethodCall(flatMapCallExpression, {
|
|
244
|
+
method: 'flatMap',
|
|
245
|
+
argumentsLength: 1,
|
|
246
|
+
optionalCall: false,
|
|
247
|
+
optionalMember: false,
|
|
248
|
+
computed: false,
|
|
249
|
+
})
|
|
250
|
+
|| isKnownNonArray(flatMapCallExpression.callee.object, context)
|
|
251
|
+
) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const filterCallExpression = flatMapCallExpression.callee.object;
|
|
256
|
+
if (
|
|
257
|
+
isFilterCallExpression(filterCallExpression)
|
|
258
|
+
&& isKnownNonArray(filterCallExpression.callee.object, context)
|
|
259
|
+
) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const [callback] = flatMapCallExpression.arguments;
|
|
264
|
+
if (!isSimpleSingleParameterArrowCallback(callback)) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const callbackResult = getCallbackResult(callback);
|
|
269
|
+
if (!callbackResult) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (callbackResult.type === 'map') {
|
|
274
|
+
return getProblemForFilterFlatMap(flatMapCallExpression, callbackResult, context) ?? {
|
|
275
|
+
node: flatMapCallExpression.callee.property,
|
|
276
|
+
messageId: MESSAGE_ID,
|
|
277
|
+
data: {method: 'map'},
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return getProblemForConditionalFlatMap(flatMapCallExpression, callback, callbackResult, context);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** @param {import('eslint').Rule.RuleContext} context */
|
|
285
|
+
const create = context => {
|
|
286
|
+
context.on('CallExpression', callExpression => getProblem(callExpression, context));
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
290
|
+
const config = {
|
|
291
|
+
create,
|
|
292
|
+
meta: {
|
|
293
|
+
type: 'suggestion',
|
|
294
|
+
docs: {
|
|
295
|
+
description: 'Disallow `Array#flatMap()` callbacks that only wrap a single item.',
|
|
296
|
+
recommended: true,
|
|
297
|
+
},
|
|
298
|
+
fixable: 'code',
|
|
299
|
+
hasSuggestions: true,
|
|
300
|
+
schema: [],
|
|
301
|
+
messages,
|
|
302
|
+
languages: [
|
|
303
|
+
'js/js',
|
|
304
|
+
],
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
export default config;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {addParenthesesToReturnOrThrowExpression, removeSpacesAfter} from './fix/index.js';
|
|
2
2
|
import {isParenthesized, needsSemicolon, isOnSameLine} from './utils/index.js';
|
|
3
3
|
|
|
4
4
|
const MESSAGE_ID = 'no-unnecessary-await';
|
|
@@ -74,7 +74,7 @@ const create = context => {
|
|
|
74
74
|
!isOnSameLine(awaitToken, valueNode, context)
|
|
75
75
|
&& !isParenthesized(node, context)
|
|
76
76
|
) {
|
|
77
|
-
yield
|
|
77
|
+
yield addParenthesesToReturnOrThrowExpression(fixer, node.parent, context);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
yield fixer.remove(awaitToken);
|