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.
Files changed (124) hide show
  1. package/package.json +26 -25
  2. package/readme.md +15 -1
  3. package/rules/ast/call-or-new-expression.js +2 -8
  4. package/rules/ast/is-member-expression.js +2 -8
  5. package/rules/comment-content.js +4 -4
  6. package/rules/consistent-boolean-name.js +727 -42
  7. package/rules/consistent-compound-words.js +1 -2
  8. package/rules/consistent-json-file-read.js +2 -5
  9. package/rules/dom-node-dataset.js +3 -7
  10. package/rules/escape-case.js +4 -4
  11. package/rules/expiring-todo-comments.js +25 -2
  12. package/rules/explicit-length-check.js +3 -3
  13. package/rules/filename-case.js +4 -4
  14. package/rules/fix/{add-parenthesizes-to-return-or-throw-expression.js → add-parentheses-to-return-or-throw-expression.js} +1 -1
  15. package/rules/fix/index.js +1 -1
  16. package/rules/fix/remove-argument.js +34 -8
  17. package/rules/fix/remove-statement.js +1 -3
  18. package/rules/fix/switch-new-expression-to-call-expression.js +2 -2
  19. package/rules/id-match.js +2 -2
  20. package/rules/index.js +10 -0
  21. package/rules/isolated-functions.js +203 -19
  22. package/rules/name-replacements.js +1 -2
  23. package/rules/no-anonymous-default-export.js +1 -3
  24. package/rules/no-array-callback-reference.js +2 -10
  25. package/rules/no-async-promise-finally.js +111 -0
  26. package/rules/no-chained-comparison.js +1 -1
  27. package/rules/no-collection-bracket-access.js +27 -4
  28. package/rules/no-computed-property-existence-check.js +1 -4
  29. package/rules/no-declarations-before-early-exit.js +6 -5
  30. package/rules/no-error-property-assignment.js +3 -3
  31. package/rules/no-for-loop.js +4 -4
  32. package/rules/no-keyword-prefix.js +2 -2
  33. package/rules/no-manually-wrapped-comments.js +5 -1
  34. package/rules/no-mismatched-map-key.js +13 -20
  35. package/rules/no-named-default.js +2 -5
  36. package/rules/no-negated-array-predicate.js +6 -13
  37. package/rules/no-negated-comparison.js +31 -71
  38. package/rules/no-negated-condition.js +2 -2
  39. package/rules/no-negation-in-equality-check.js +4 -7
  40. package/rules/no-non-function-verb-prefix.js +25 -2
  41. package/rules/no-process-exit.js +6 -6
  42. package/rules/no-return-array-push.js +9 -2
  43. package/rules/no-static-only-class.js +2 -8
  44. package/rules/no-thenable.js +9 -15
  45. package/rules/no-typeof-undefined.js +2 -2
  46. package/rules/no-undeclared-class-members.js +1 -5
  47. package/rules/no-unnecessary-array-flat-map.js +308 -0
  48. package/rules/no-unnecessary-await.js +2 -2
  49. package/rules/no-unnecessary-fetch-options.js +624 -0
  50. package/rules/no-unnecessary-global-this.js +5 -9
  51. package/rules/no-unsafe-promise-all-settled-values.js +865 -0
  52. package/rules/no-unused-properties.js +0 -1
  53. package/rules/no-useless-coercion.js +1 -2
  54. package/rules/no-useless-else.js +32 -1
  55. package/rules/no-useless-iterator-to-array.js +42 -16
  56. package/rules/no-useless-spread.js +2 -2
  57. package/rules/no-useless-undefined.js +2 -2
  58. package/rules/prefer-abort-signal-any.js +1391 -0
  59. package/rules/prefer-array-find.js +2 -5
  60. package/rules/prefer-array-flat-map.js +6 -11
  61. package/rules/prefer-array-from-range.js +98 -0
  62. package/rules/prefer-bigint-literals.js +1 -0
  63. package/rules/prefer-block-statement-over-iife.js +159 -0
  64. package/rules/prefer-boolean-return.js +20 -3
  65. package/rules/prefer-continue.js +6 -2
  66. package/rules/prefer-default-parameters.js +2 -2
  67. package/rules/prefer-early-return.js +3 -1
  68. package/rules/prefer-else-if.js +5 -9
  69. package/rules/prefer-group-by.js +576 -0
  70. package/rules/prefer-has-check.js +6 -3
  71. package/rules/prefer-https.js +3 -3
  72. package/rules/prefer-identifier-import-export-specifiers.js +1 -3
  73. package/rules/prefer-iterator-helpers.js +196 -0
  74. package/rules/prefer-minimal-ternary.js +4 -4
  75. package/rules/prefer-number-coercion.js +1 -1
  76. package/rules/prefer-object-define-properties.js +1 -3
  77. package/rules/prefer-object-iterable-methods.js +57 -14
  78. package/rules/prefer-observer-apis.js +2 -5
  79. package/rules/prefer-private-class-fields.js +1 -3
  80. package/rules/prefer-simple-sort-comparator.js +6 -6
  81. package/rules/prefer-simplified-conditions.js +584 -0
  82. package/rules/prefer-single-object-destructuring.js +2 -5
  83. package/rules/prefer-single-replace.js +2 -2
  84. package/rules/prefer-spread.js +25 -32
  85. package/rules/prefer-string-pad-start-end.js +1 -6
  86. package/rules/prefer-string-raw.js +2 -2
  87. package/rules/prefer-string-repeat.js +16 -9
  88. package/rules/prefer-string-replace-all.js +1 -1
  89. package/rules/prefer-ternary.js +5 -5
  90. package/rules/prefer-toggle-attribute.js +3 -3
  91. package/rules/prefer-top-level-await.js +6 -1
  92. package/rules/prefer-uint8array-base64.js +3 -1
  93. package/rules/require-proxy-trap-boolean-return.js +2 -2
  94. package/rules/rule/to-eslint-create.js +1 -2
  95. package/rules/rule/to-eslint-listener.js +1 -3
  96. package/rules/rule/to-eslint-problem.js +1 -3
  97. package/rules/rule/to-eslint-rule-fixer.js +1 -3
  98. package/rules/rule/to-eslint-rule.js +1 -3
  99. package/rules/rule/to-eslint-rules.js +1 -3
  100. package/rules/rule/unicorn-context.js +1 -3
  101. package/rules/rule/unicorn-listeners.js +1 -3
  102. package/rules/shared/array-range.js +66 -0
  103. package/rules/shared/iterator-helpers.js +43 -1
  104. package/rules/shared/no-array-mutate-rule.js +1 -3
  105. package/rules/shared/regexp-escape.js +4 -4
  106. package/rules/utils/can-tokens-be-adjacent.js +95 -0
  107. package/rules/utils/comparison.js +65 -0
  108. package/rules/utils/escape-string.js +2 -8
  109. package/rules/utils/get-available-variable-name.js +2 -13
  110. package/rules/utils/get-precedence.js +112 -0
  111. package/rules/utils/get-token-store.js +1 -3
  112. package/rules/utils/global-reference-tracker.js +3 -6
  113. package/rules/utils/index.js +4 -0
  114. package/rules/utils/is-boolean.js +103 -30
  115. package/rules/utils/is-identifier-name.js +5 -0
  116. package/rules/utils/is-promise-type.js +1 -3
  117. package/rules/utils/is-react-hook-name.js +5 -0
  118. package/rules/utils/parentheses/get-parent-syntax-opening-parenthesis.js +1 -3
  119. package/rules/utils/parentheses/iterate-surrounding-parentheses.js +1 -1
  120. package/rules/utils/should-add-parentheses-to-await-expression-argument.js +4 -9
  121. package/rules/utils/should-add-parentheses-to-call-expression-callee.js +6 -12
  122. package/rules/utils/should-add-parentheses-to-unary-expression.js +7 -14
  123. package/rules/utils/to-location.js +1 -3
  124. package/rules/utils/track-branch-exits.js +8 -7
@@ -65,7 +65,6 @@ const getReferenceParent = referenceNode => {
65
65
  return referenceNode.parent;
66
66
  };
67
67
 
68
- // eslint-disable-next-line unicorn/name-replacements
69
68
  const specialProtoPropertyKey = {
70
69
  type: 'Identifier',
71
70
  name: '__proto__',
@@ -1,3 +1,4 @@
1
+ import {isEmptyStringLiteral} from './ast/index.js';
1
2
  import {
2
3
  isBoolean,
3
4
  isString,
@@ -25,8 +26,6 @@ const coercions = new Map([
25
26
  ['Number', {type: 'number', isType: (node, context) => isNumber(node, context.sourceCode.getScope(node))}],
26
27
  ]);
27
28
 
28
- const isEmptyStringLiteral = node => node.type === 'Literal' && node.value === '';
29
-
30
29
  /** @param {ESLint.Rule.RuleContext} context */
31
30
  const create = context => {
32
31
  const {sourceCode} = context;
@@ -1,6 +1,8 @@
1
+ import {isMethodCall} from './ast/index.js';
1
2
  import {
2
3
  hasCommentInRange,
3
4
  hasDirectBlockScopedDeclaration,
5
+ isGlobalIdentifier,
4
6
  needsSemicolon,
5
7
  trackBranchExits,
6
8
  } from './utils/index.js';
@@ -42,6 +44,35 @@ const isJsxChildTextToken = (token, sourceCode) =>
42
44
  token.type === 'JSXText'
43
45
  && sourceCode.getNodeByRangeIndex(sourceCode.getRange(token)[0]).type === 'JSXText';
44
46
 
47
+ const isProcessExitCall = (node, context) =>
48
+ isMethodCall(node, {
49
+ object: 'process',
50
+ method: 'exit',
51
+ optionalCall: false,
52
+ optionalMember: false,
53
+ })
54
+ && isGlobalIdentifier(node.callee.object, context);
55
+
56
+ const isProcessExitStatement = (node, context) =>
57
+ node.type === 'ExpressionStatement'
58
+ && isProcessExitCall(node.expression, context);
59
+
60
+ function branchExits(branch, context, branchAlwaysExits) {
61
+ if (branchAlwaysExits(branch) || isProcessExitStatement(branch, context)) {
62
+ return true;
63
+ }
64
+
65
+ if (branch.type === 'BlockStatement') {
66
+ const lastStatement = branch.body.at(-1);
67
+ return Boolean(lastStatement && branchExits(lastStatement, context, branchAlwaysExits));
68
+ }
69
+
70
+ return branch.type === 'IfStatement'
71
+ && branch.alternate
72
+ && branchExits(branch.consequent, context, branchAlwaysExits)
73
+ && branchExits(branch.alternate, context, branchAlwaysExits);
74
+ }
75
+
45
76
  // A multiline token's internal whitespace can be meaningful, so it can't be safely reindented.
46
77
  // JSX child text is safe because JSX collapses line-leading and line-trailing whitespace at compile time.
47
78
  const hasReindentUnsafeMultilineToken = (node, context) => {
@@ -192,7 +223,7 @@ const fix = (ifStatement, context) => fixer => {
192
223
  /** @param {ESLint.Rule.RuleContext} context */
193
224
  const create = context => {
194
225
  const {sourceCode} = context;
195
- const branchAlwaysExits = trackBranchExits(context);
226
+ const branchAlwaysExits = trackBranchExits(context, branch => branchExits(branch, context, branchAlwaysExits));
196
227
 
197
228
  context.onExit('IfStatement', ifStatement => {
198
229
  if (!(
@@ -1,4 +1,5 @@
1
1
  import typedArray from './shared/typed-array.js';
2
+ import {unwrapExpression} from './shared/iterator-helpers.js';
2
3
  import {removeMethodCall} from './fix/index.js';
3
4
  import {
4
5
  isNewExpression,
@@ -33,9 +34,7 @@ const callbackOnlyIteratorMethods = [
33
34
  'some',
34
35
  ];
35
36
 
36
- // `reduce` — both Array and Iterator accept (callback, initialValue),
37
- // but Array.reduce without initialValue uses the first element while
38
- // Iterator.reduce without initialValue throws.
37
+ // `reduce` — both Array and Iterator accept (callback, initialValue).
39
38
  const reduceMethod = 'reduce';
40
39
 
41
40
  const isToArrayCall = node => isMethodCall(node, {
@@ -81,13 +80,17 @@ const getArrayFromProblem = (node, context) => {
81
80
  }
82
81
 
83
82
  const toArrayCall = node.arguments[0];
84
- const fix = getRemoveToArrayFix(toArrayCall, context);
83
+ const fix = node.arguments.length === 1 ? getRemoveToArrayFix(toArrayCall, context) : undefined;
84
+ const suggestion = node.arguments.length > 1
85
+ ? getRemoveToArraySuggestion(toArrayCall, context, MESSAGE_ID_SUGGESTION_ITERABLE_ACCEPTING)
86
+ : undefined;
85
87
 
86
88
  return {
87
89
  node: toArrayCall.callee.property,
88
90
  messageId: MESSAGE_ID_ITERABLE_ACCEPTING,
89
91
  data: {description: `${node.callee.object.name}.${node.callee.property.name}(…)`},
90
92
  ...(fix && {fix}),
93
+ ...(suggestion && {suggest: [suggestion]}),
91
94
  };
92
95
  };
93
96
 
@@ -106,13 +109,17 @@ const getObjectFromEntriesProblem = (node, context) => {
106
109
  }
107
110
 
108
111
  const toArrayCall = node.arguments[0];
109
- const fix = getRemoveToArrayFix(toArrayCall, context);
112
+ const fix = node.arguments.length === 1 ? getRemoveToArrayFix(toArrayCall, context) : undefined;
113
+ const suggestion = node.arguments.length > 1
114
+ ? getRemoveToArraySuggestion(toArrayCall, context, MESSAGE_ID_SUGGESTION_ITERABLE_ACCEPTING)
115
+ : undefined;
110
116
 
111
117
  return {
112
118
  node: toArrayCall.callee.property,
113
119
  messageId: MESSAGE_ID_ITERABLE_ACCEPTING,
114
120
  data: {description: `${node.callee.object.name}.${node.callee.property.name}(…)`},
115
121
  ...(fix && {fix}),
122
+ ...(suggestion && {suggest: [suggestion]}),
116
123
  };
117
124
  };
118
125
 
@@ -143,6 +150,28 @@ const getPromiseProblem = (node, context) => {
143
150
  };
144
151
  };
145
152
 
153
+ const canObserveArrayArgument = (callback, arrayParameterIndex, context) => {
154
+ if (!callback) {
155
+ return false;
156
+ }
157
+
158
+ callback = unwrapExpression(callback);
159
+
160
+ if (
161
+ callback.type === 'ArrowFunctionExpression'
162
+ || callback.type === 'FunctionExpression'
163
+ ) {
164
+ return callback.params.length > arrayParameterIndex
165
+ || callback.params.at(-1)?.type === 'RestElement'
166
+ || (
167
+ callback.type === 'FunctionExpression'
168
+ && context.sourceCode.getTokens(callback).some(token => token.type === 'Identifier' && token.value === 'arguments')
169
+ );
170
+ }
171
+
172
+ return false;
173
+ };
174
+
146
175
  const getIteratorMethodProblem = (node, context) => {
147
176
  if (
148
177
  !(
@@ -154,7 +183,8 @@ const getIteratorMethodProblem = (node, context) => {
154
183
  })
155
184
  || isMethodCall(node, {
156
185
  method: reduceMethod,
157
- argumentsLength: 2,
186
+ minimumArguments: 1,
187
+ maximumArguments: 2,
158
188
  optionalCall: false,
159
189
  optionalMember: false,
160
190
  })
@@ -170,15 +200,7 @@ const getIteratorMethodProblem = (node, context) => {
170
200
  const method = node.callee.property.name;
171
201
  const isReduceCall = method === reduceMethod;
172
202
  const arrayParameterIndex = isReduceCall ? 3 : 2;
173
- if (
174
- callback
175
- && (callback.type === 'ArrowFunctionExpression' || callback.type === 'FunctionExpression')
176
- && (
177
- callback.params.length > arrayParameterIndex
178
- // A rest parameter can capture the trailing `array` argument too.
179
- || callback.params.at(-1)?.type === 'RestElement'
180
- )
181
- ) {
203
+ if (canObserveArrayArgument(callback, arrayParameterIndex, context)) {
182
204
  return;
183
205
  }
184
206
 
@@ -208,13 +230,17 @@ const create = context => {
208
230
  }
209
231
 
210
232
  const toArrayCall = node.arguments[0];
211
- const fix = getRemoveToArrayFix(toArrayCall, context);
233
+ const fix = node.arguments.length === 1 ? getRemoveToArrayFix(toArrayCall, context) : undefined;
234
+ const suggestion = node.arguments.length > 1
235
+ ? getRemoveToArraySuggestion(toArrayCall, context, MESSAGE_ID_SUGGESTION_ITERABLE_ACCEPTING)
236
+ : undefined;
212
237
 
213
238
  return {
214
239
  node: toArrayCall.callee.property,
215
240
  messageId: MESSAGE_ID_ITERABLE_ACCEPTING,
216
241
  data: {description: `new ${node.callee.name}(…)`},
217
242
  ...(fix && {fix}),
243
+ ...(suggestion && {suggest: [suggestion]}),
218
244
  };
219
245
  });
220
246
 
@@ -1,6 +1,6 @@
1
1
  import {isCommaToken} from '@eslint-community/eslint-utils';
2
2
  import typedArray from './shared/typed-array.js';
3
- import {removeParentheses, fixSpaceAroundKeyword, addParenthesizesToReturnOrThrowExpression} from './fix/index.js';
3
+ import {removeParentheses, fixSpaceAroundKeyword, addParenthesesToReturnOrThrowExpression} from './fix/index.js';
4
4
  import {
5
5
  isArray,
6
6
  isKnownNonArray,
@@ -143,7 +143,7 @@ function * unwrapSingleArraySpread(fixer, arrayExpression, context) {
143
143
  && !isOnSameLine(openingBracketToken, thirdToken, context)
144
144
  && !isParenthesized(arrayExpression, context)
145
145
  ) {
146
- yield addParenthesizesToReturnOrThrowExpression(fixer, parent, context);
146
+ yield addParenthesesToReturnOrThrowExpression(fixer, parent, context);
147
147
  return;
148
148
  }
149
149
 
@@ -410,10 +410,10 @@ const create = context => {
410
410
  return;
411
411
  }
412
412
 
413
- const accessWhenTestPasses = !isConsequentUndefined;
413
+ const isAccessWhenTestPasses = !isConsequentUndefined;
414
414
  const testValidity = getIndexedAccessTestValidity(node.test, indexedAccess);
415
415
 
416
- if (testValidity !== accessWhenTestPasses) {
416
+ if (testValidity !== isAccessWhenTestPasses) {
417
417
  return;
418
418
  }
419
419