eslint-plugin-unicorn 70.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 (91) hide show
  1. package/package.json +26 -25
  2. package/readme.md +5 -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 +94 -10
  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 +2 -2
  12. package/rules/explicit-length-check.js +3 -3
  13. package/rules/filename-case.js +4 -4
  14. package/rules/fix/remove-argument.js +34 -8
  15. package/rules/fix/remove-statement.js +1 -3
  16. package/rules/id-match.js +2 -2
  17. package/rules/isolated-functions.js +47 -7
  18. package/rules/name-replacements.js +1 -2
  19. package/rules/no-anonymous-default-export.js +1 -3
  20. package/rules/no-array-callback-reference.js +2 -10
  21. package/rules/no-chained-comparison.js +1 -1
  22. package/rules/no-computed-property-existence-check.js +1 -4
  23. package/rules/no-declarations-before-early-exit.js +6 -5
  24. package/rules/no-error-property-assignment.js +3 -3
  25. package/rules/no-for-loop.js +4 -4
  26. package/rules/no-keyword-prefix.js +2 -2
  27. package/rules/no-mismatched-map-key.js +13 -20
  28. package/rules/no-named-default.js +2 -5
  29. package/rules/no-negated-array-predicate.js +4 -11
  30. package/rules/no-negated-comparison.js +7 -10
  31. package/rules/no-negation-in-equality-check.js +2 -5
  32. package/rules/no-process-exit.js +6 -6
  33. package/rules/no-static-only-class.js +2 -8
  34. package/rules/no-thenable.js +9 -15
  35. package/rules/no-undeclared-class-members.js +1 -5
  36. package/rules/no-unnecessary-array-flat-map.js +6 -5
  37. package/rules/no-unnecessary-global-this.js +5 -9
  38. package/rules/no-unused-properties.js +0 -1
  39. package/rules/no-useless-coercion.js +1 -2
  40. package/rules/no-useless-undefined.js +2 -2
  41. package/rules/prefer-array-find.js +2 -5
  42. package/rules/prefer-bigint-literals.js +1 -0
  43. package/rules/prefer-boolean-return.js +20 -3
  44. package/rules/prefer-default-parameters.js +2 -2
  45. package/rules/prefer-else-if.js +5 -9
  46. package/rules/prefer-has-check.js +6 -3
  47. package/rules/prefer-https.js +3 -3
  48. package/rules/prefer-identifier-import-export-specifiers.js +1 -3
  49. package/rules/prefer-minimal-ternary.js +4 -4
  50. package/rules/prefer-object-define-properties.js +1 -3
  51. package/rules/prefer-observer-apis.js +2 -5
  52. package/rules/prefer-private-class-fields.js +1 -3
  53. package/rules/prefer-simple-sort-comparator.js +6 -6
  54. package/rules/prefer-single-object-destructuring.js +2 -5
  55. package/rules/prefer-single-replace.js +2 -2
  56. package/rules/prefer-spread.js +22 -32
  57. package/rules/prefer-string-pad-start-end.js +1 -6
  58. package/rules/prefer-string-repeat.js +16 -9
  59. package/rules/prefer-string-replace-all.js +1 -1
  60. package/rules/prefer-ternary.js +5 -5
  61. package/rules/prefer-toggle-attribute.js +3 -3
  62. package/rules/prefer-top-level-await.js +6 -1
  63. package/rules/prefer-uint8array-base64.js +3 -1
  64. package/rules/require-proxy-trap-boolean-return.js +2 -2
  65. package/rules/rule/to-eslint-create.js +1 -2
  66. package/rules/rule/to-eslint-listener.js +1 -3
  67. package/rules/rule/to-eslint-problem.js +1 -3
  68. package/rules/rule/to-eslint-rule-fixer.js +1 -3
  69. package/rules/rule/to-eslint-rule.js +1 -3
  70. package/rules/rule/to-eslint-rules.js +1 -3
  71. package/rules/rule/unicorn-context.js +1 -3
  72. package/rules/rule/unicorn-listeners.js +1 -3
  73. package/rules/shared/no-array-mutate-rule.js +1 -3
  74. package/rules/shared/regexp-escape.js +4 -4
  75. package/rules/utils/can-tokens-be-adjacent.js +95 -0
  76. package/rules/utils/escape-string.js +2 -8
  77. package/rules/utils/get-available-variable-name.js +2 -13
  78. package/rules/utils/get-precedence.js +112 -0
  79. package/rules/utils/get-token-store.js +1 -3
  80. package/rules/utils/global-reference-tracker.js +3 -6
  81. package/rules/utils/index.js +4 -0
  82. package/rules/utils/is-boolean.js +90 -32
  83. package/rules/utils/is-identifier-name.js +5 -0
  84. package/rules/utils/is-promise-type.js +1 -3
  85. package/rules/utils/is-react-hook-name.js +5 -0
  86. package/rules/utils/parentheses/get-parent-syntax-opening-parenthesis.js +1 -3
  87. package/rules/utils/parentheses/iterate-surrounding-parentheses.js +1 -1
  88. package/rules/utils/should-add-parentheses-to-await-expression-argument.js +4 -9
  89. package/rules/utils/should-add-parentheses-to-call-expression-callee.js +6 -12
  90. package/rules/utils/should-add-parentheses-to-unary-expression.js +7 -14
  91. package/rules/utils/to-location.js +1 -3
@@ -16,6 +16,10 @@ const booleanBinaryOperators = new Set([
16
16
 
17
17
  const booleanTypeStrings = new Set(['boolean', 'true', 'false']);
18
18
  const booleanGlobalFunctions = new Set(['isFinite', 'isNaN']);
19
+ const nullishTypeAnnotationTypes = new Set([
20
+ 'TSNullKeyword',
21
+ 'TSUndefinedKeyword',
22
+ ]);
19
23
 
20
24
  const booleanStaticMethods = new Map([
21
25
  ['Array', new Set(['isArray'])],
@@ -90,28 +94,16 @@ function isBooleanCallSignatureMembers(members, context, scope, visitedTypeRefer
90
94
  && callSignatures.every(member => isBooleanTypeAnnotation(member.returnType, context, scope, visitedTypeReferenceNames));
91
95
  }
92
96
 
93
- function isBooleanTypeReference(node, context, scope, visitedTypeReferenceNames) {
94
- const name = getTypeReferenceName(node.typeName);
95
- if (!name || visitedTypeReferenceNames.has(name)) {
96
- return false;
97
- }
98
-
99
- visitedTypeReferenceNames.add(name);
100
-
101
- const variable = resolveVariableName(name, scope);
102
- const [definition] = variable?.defs ?? [];
103
- const definitionScope = definition?.type === 'Type'
104
- ? context.sourceCode.getScope(definition.node)
105
- : scope;
106
- const result = definition?.type === 'Type'
107
- && definition.node.type === 'TSTypeAliasDeclaration'
108
- && isBooleanTypeAnnotation(definition.node.typeAnnotation, context, definitionScope, visitedTypeReferenceNames);
109
-
110
- visitedTypeReferenceNames.delete(name);
111
- return result;
112
- }
97
+ const hasCallSignatureMembers = members =>
98
+ members.some(member => member.type === 'TSCallSignatureDeclaration');
113
99
 
114
- function isBooleanFunctionTypeReference(node, context, scope, visitedTypeReferenceNames) {
100
+ function isTypeReference({
101
+ node,
102
+ context,
103
+ scope,
104
+ visitedTypeReferenceNames,
105
+ predicate,
106
+ }) {
115
107
  const name = getTypeReferenceName(node.typeName);
116
108
  if (!name || visitedTypeReferenceNames.has(name)) {
117
109
  return false;
@@ -124,22 +116,55 @@ function isBooleanFunctionTypeReference(node, context, scope, visitedTypeReferen
124
116
  const definitionScope = definition?.type === 'Type'
125
117
  ? context.sourceCode.getScope(definition.node)
126
118
  : scope;
127
- const result = definition?.type === 'Type'
128
- && (
129
- (
130
- definition.node.type === 'TSTypeAliasDeclaration'
131
- && isBooleanFunctionTypeAnnotation(definition.node.typeAnnotation, context, definitionScope, visitedTypeReferenceNames)
132
- )
133
- || (
134
- definition.node.type === 'TSInterfaceDeclaration'
135
- && isBooleanCallSignatureMembers(definition.node.body.body, context, definitionScope, visitedTypeReferenceNames)
136
- )
137
- );
119
+ const result = definition?.type === 'Type' && predicate(definition.node, definitionScope);
138
120
 
139
121
  visitedTypeReferenceNames.delete(name);
140
122
  return result;
141
123
  }
142
124
 
125
+ const isBooleanTypeReference = (node, context, scope, visitedTypeReferenceNames) => isTypeReference(
126
+ {
127
+ node,
128
+ context,
129
+ scope,
130
+ visitedTypeReferenceNames,
131
+ predicate: (definitionNode, definitionScope) => definitionNode.type === 'TSTypeAliasDeclaration'
132
+ && isBooleanTypeAnnotation(definitionNode.typeAnnotation, context, definitionScope, visitedTypeReferenceNames),
133
+ },
134
+ );
135
+
136
+ const isBooleanFunctionTypeReference = (node, context, scope, visitedTypeReferenceNames) => isTypeReference(
137
+ {
138
+ node,
139
+ context,
140
+ scope,
141
+ visitedTypeReferenceNames,
142
+ predicate: (definitionNode, definitionScope) => (
143
+ definitionNode.type === 'TSTypeAliasDeclaration'
144
+ && isBooleanFunctionTypeAnnotation(definitionNode.typeAnnotation, context, definitionScope, visitedTypeReferenceNames)
145
+ ) || (
146
+ definitionNode.type === 'TSInterfaceDeclaration'
147
+ && isBooleanCallSignatureMembers(definitionNode.body.body, context, definitionScope, visitedTypeReferenceNames)
148
+ ),
149
+ },
150
+ );
151
+
152
+ const isFunctionTypeReference = (node, context, scope, visitedTypeReferenceNames) => isTypeReference(
153
+ {
154
+ node,
155
+ context,
156
+ scope,
157
+ visitedTypeReferenceNames,
158
+ predicate: (definitionNode, definitionScope) => (
159
+ definitionNode.type === 'TSTypeAliasDeclaration'
160
+ && isFunctionTypeAnnotation(definitionNode.typeAnnotation, context, definitionScope, visitedTypeReferenceNames)
161
+ ) || (
162
+ definitionNode.type === 'TSInterfaceDeclaration'
163
+ && hasCallSignatureMembers(definitionNode.body.body)
164
+ ),
165
+ },
166
+ );
167
+
143
168
  function isBooleanTypeAnnotation(node, context, scope, visitedTypeReferenceNames = new Set()) {
144
169
  switch (node?.type) {
145
170
  case 'TSTypeAnnotation':
@@ -177,6 +202,38 @@ function isBooleanTypeAnnotation(node, context, scope, visitedTypeReferenceNames
177
202
  }
178
203
  }
179
204
 
205
+ function isFunctionTypeAnnotation(node, context, scope, visitedTypeReferenceNames = new Set()) {
206
+ switch (node?.type) {
207
+ case 'TSTypeAnnotation':
208
+ case 'TSParenthesizedType': {
209
+ return isFunctionTypeAnnotation(node.typeAnnotation, context, scope, visitedTypeReferenceNames);
210
+ }
211
+
212
+ case 'TSFunctionType': {
213
+ return true;
214
+ }
215
+
216
+ case 'TSTypeLiteral': {
217
+ return hasCallSignatureMembers(node.members);
218
+ }
219
+
220
+ case 'TSTypeReference': {
221
+ return isFunctionTypeReference(node, context, scope, visitedTypeReferenceNames);
222
+ }
223
+
224
+ case 'TSUnionType': {
225
+ const types = node.types.filter(type => !nullishTypeAnnotationTypes.has(type.type));
226
+
227
+ return types.length > 0
228
+ && types.every(type => isFunctionTypeAnnotation(type, context, scope, visitedTypeReferenceNames));
229
+ }
230
+
231
+ default: {
232
+ return false;
233
+ }
234
+ }
235
+ }
236
+
180
237
  function isBooleanFunctionTypeAnnotation(node, context, scope, visitedTypeReferenceNames = new Set()) {
181
238
  switch (node?.type) {
182
239
  case 'TSTypeAnnotation':
@@ -599,6 +656,7 @@ export {
599
656
  isBooleanFunctionTypeAnnotation,
600
657
  isKnownBooleanFunctionReference,
601
658
  isBooleanTypeAnnotation,
659
+ isFunctionTypeAnnotation,
602
660
  };
603
661
 
604
662
  export default isBooleanExpression;
@@ -0,0 +1,5 @@
1
+ import isIdentifier from 'is-identifier';
2
+
3
+ export default function isIdentifierName(name) {
4
+ return isIdentifier(name, {checkReserved: false});
5
+ }
@@ -1,7 +1,7 @@
1
1
  import {isUnknownType} from './types.js';
2
2
 
3
3
  // Best-effort check whether a TypeScript type is a promise. Returns `true`/`false` when known, `undefined` when indeterminate.
4
- function isPromiseType(type, checker) {
4
+ export default function isPromiseType(type, checker) {
5
5
  type = checker.getNonNullableType(type);
6
6
 
7
7
  if (isUnknownType(type)) {
@@ -24,5 +24,3 @@ function isPromiseType(type, checker) {
24
24
 
25
25
  return checker.getPromisedTypeOfPromise(type) !== undefined;
26
26
  }
27
-
28
- export default isPromiseType;
@@ -0,0 +1,5 @@
1
+ // https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts
2
+ const isReactHookName = name =>
3
+ name === 'use' || /^use[\dA-Z]/.test(name);
4
+
5
+ export default isReactHookName;
@@ -15,7 +15,7 @@ E.g., `if (a) {}` then the `(`.
15
15
  @param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
16
16
  @returns {OpeningParenthesisToken | void} The left parenthesis of the parent node syntax
17
17
  */
18
- function getParentSyntaxOpeningParenthesis(node, context) {
18
+ export default function getParentSyntaxOpeningParenthesis(node, context) {
19
19
  const {parent} = node;
20
20
 
21
21
  switch (parent.type) {
@@ -76,5 +76,3 @@ function getParentSyntaxOpeningParenthesis(node, context) {
76
76
  // No default
77
77
  }
78
78
  }
79
-
80
- export default getParentSyntaxOpeningParenthesis;
@@ -35,7 +35,7 @@ function getSurroundingParentheses([head, tail], context) {
35
35
 
36
36
  const tokenAfter = context.sourceCode.getTokenAfter(tail);
37
37
 
38
- if (!tokenBefore || !isClosingParenthesisToken(tokenAfter)) {
38
+ if (!tokenAfter || !isClosingParenthesisToken(tokenAfter)) {
39
39
  return;
40
40
  }
41
41
 
@@ -1,3 +1,5 @@
1
+ import getPrecedence, {PRECEDENCE_UNARY} from './get-precedence.js';
2
+
1
3
  /**
2
4
  Check if parentheses should be added to a `node` when it's used as `argument` of `AwaitExpression`.
3
5
 
@@ -5,13 +7,6 @@ Check if parentheses should be added to a `node` when it's used as `argument` of
5
7
  @returns {boolean}
6
8
  */
7
9
  export default function shouldAddParenthesesToAwaitExpressionArgument(node) {
8
- return [
9
- 'SequenceExpression',
10
- 'YieldExpression',
11
- 'ArrowFunctionExpression',
12
- 'ConditionalExpression',
13
- 'AssignmentExpression',
14
- 'LogicalExpression',
15
- 'BinaryExpression',
16
- ].includes(node.type);
10
+ // `await` has the same precedence as other unary operators.
11
+ return getPrecedence(node) < PRECEDENCE_UNARY;
17
12
  }
@@ -1,3 +1,5 @@
1
+ import getPrecedence, {PRECEDENCE_CALL} from './get-precedence.js';
2
+
1
3
  /**
2
4
  Check if parentheses should be added to a `node` when it's used as `callee` of `CallExpression`.
3
5
 
@@ -5,16 +7,8 @@ Check if parentheses should be added to a `node` when it's used as `callee` of `
5
7
  @returns {boolean}
6
8
  */
7
9
  export default function shouldAddParenthesesToCallExpressionCallee(node) {
8
- return [
9
- 'SequenceExpression',
10
- 'YieldExpression',
11
- 'ArrowFunctionExpression',
12
- 'ConditionalExpression',
13
- 'AssignmentExpression',
14
- 'LogicalExpression',
15
- 'BinaryExpression',
16
- 'UnaryExpression',
17
- 'UpdateExpression',
18
- 'NewExpression',
19
- ].includes(node.type);
10
+ // `new Foo` (without its own call parentheses) would absorb the call's `()` into its own
11
+ // argument list (`new Foo()` instead of `(new Foo)()`), so it always needs parentheses
12
+ // regardless of its precedence.
13
+ return getPrecedence(node) < PRECEDENCE_CALL || node.type === 'NewExpression';
20
14
  }
@@ -1,3 +1,5 @@
1
+ import getPrecedence, {PRECEDENCE_UNARY} from './get-precedence.js';
2
+
1
3
  /**
2
4
  Check if parentheses should be added to a `node` when it's used as `argument` of `UnaryExpression`.
3
5
 
@@ -8,23 +10,14 @@ Check if parentheses should be added to a `node` when it's used as `argument` of
8
10
  // Prefix unary operators all share the same precedence, so the same parenthesization rules apply.
9
11
  const supportedOperators = new Set(['!', 'typeof', 'void', 'delete', '~', '+', '-']);
10
12
 
13
+ // These are same or higher precedence than `UnaryExpression`, but still look ambiguous as its
14
+ // argument without parentheses (`!x++`, `typeof x!`, `!<Foo>x`), so they get parenthesized too.
15
+ const alwaysParenthesizedTypes = new Set(['UpdateExpression', 'TSNonNullExpression', 'TSTypeAssertion']);
16
+
11
17
  export default function shouldAddParenthesesToUnaryExpressionArgument(node, operator) {
12
18
  if (!supportedOperators.has(operator)) {
13
19
  throw new Error('Unexpected operator');
14
20
  }
15
21
 
16
- return [
17
- 'UpdateExpression',
18
- 'BinaryExpression',
19
- 'LogicalExpression',
20
- 'ConditionalExpression',
21
- 'AssignmentExpression',
22
- 'ArrowFunctionExpression',
23
- 'YieldExpression',
24
- 'SequenceExpression',
25
- 'TSAsExpression',
26
- 'TSSatisfiesExpression',
27
- 'TSNonNullExpression',
28
- 'TSTypeAssertion',
29
- ].includes(node.type);
22
+ return getPrecedence(node) < PRECEDENCE_UNARY || alwaysParenthesizedTypes.has(node.type);
30
23
  }
@@ -12,7 +12,7 @@ Get location info for the given node or range.
12
12
  @param {int} [endOffset] - End position offset.
13
13
  @returns {ESTree.SourceLocation}
14
14
  */
15
- function toLocation(nodeOrRange, context, startOffset = 0, endOffset = 0) {
15
+ export default function toLocation(nodeOrRange, context, startOffset = 0, endOffset = 0) {
16
16
  const {sourceCode} = context;
17
17
  const [start, end] = Array.isArray(nodeOrRange) ? nodeOrRange : sourceCode.getRange(nodeOrRange);
18
18
 
@@ -21,5 +21,3 @@ function toLocation(nodeOrRange, context, startOffset = 0, endOffset = 0) {
21
21
  end: sourceCode.getLocFromIndex(end + endOffset),
22
22
  };
23
23
  }
24
-
25
- export default toLocation;