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.
- package/package.json +26 -25
- package/readme.md +5 -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 +94 -10
- 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 +2 -2
- package/rules/explicit-length-check.js +3 -3
- package/rules/filename-case.js +4 -4
- package/rules/fix/remove-argument.js +34 -8
- package/rules/fix/remove-statement.js +1 -3
- package/rules/id-match.js +2 -2
- package/rules/isolated-functions.js +47 -7
- 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-chained-comparison.js +1 -1
- 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-mismatched-map-key.js +13 -20
- package/rules/no-named-default.js +2 -5
- package/rules/no-negated-array-predicate.js +4 -11
- package/rules/no-negated-comparison.js +7 -10
- package/rules/no-negation-in-equality-check.js +2 -5
- package/rules/no-process-exit.js +6 -6
- package/rules/no-static-only-class.js +2 -8
- package/rules/no-thenable.js +9 -15
- package/rules/no-undeclared-class-members.js +1 -5
- package/rules/no-unnecessary-array-flat-map.js +6 -5
- package/rules/no-unnecessary-global-this.js +5 -9
- package/rules/no-unused-properties.js +0 -1
- package/rules/no-useless-coercion.js +1 -2
- package/rules/no-useless-undefined.js +2 -2
- package/rules/prefer-array-find.js +2 -5
- package/rules/prefer-bigint-literals.js +1 -0
- package/rules/prefer-boolean-return.js +20 -3
- package/rules/prefer-default-parameters.js +2 -2
- package/rules/prefer-else-if.js +5 -9
- 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-minimal-ternary.js +4 -4
- package/rules/prefer-object-define-properties.js +1 -3
- 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-single-object-destructuring.js +2 -5
- package/rules/prefer-single-replace.js +2 -2
- package/rules/prefer-spread.js +22 -32
- package/rules/prefer-string-pad-start-end.js +1 -6
- 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/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/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 +90 -32
- 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
|
@@ -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
|
-
|
|
94
|
-
|
|
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
|
|
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;
|
|
@@ -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;
|
|
@@ -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 (!
|
|
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
|
-
|
|
9
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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;
|