eslint-plugin-unicorn 70.0.0 → 71.1.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 +275 -29
- 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
|
@@ -24,13 +24,6 @@ const replacementMethod = new Map([
|
|
|
24
24
|
]);
|
|
25
25
|
const methods = replacementMethod.keys().toArray();
|
|
26
26
|
|
|
27
|
-
const predicateBodyTypesRequiringParentheses = new Set([
|
|
28
|
-
'TSAsExpression',
|
|
29
|
-
'TSNonNullExpression',
|
|
30
|
-
'TSSatisfiesExpression',
|
|
31
|
-
'TSTypeAssertion',
|
|
32
|
-
]);
|
|
33
|
-
|
|
34
27
|
const needsParenthesesInConciseArrowBody = (node, text) =>
|
|
35
28
|
node.parent.type === 'ArrowFunctionExpression'
|
|
36
29
|
&& node.parent.body === node
|
|
@@ -75,7 +68,7 @@ function getReplacementPredicateText(node, context) {
|
|
|
75
68
|
}
|
|
76
69
|
|
|
77
70
|
const text = context.sourceCode.getText(node);
|
|
78
|
-
const needsParentheses =
|
|
71
|
+
const needsParentheses = shouldAddParenthesesToUnaryExpressionArgument(node, '!');
|
|
79
72
|
|
|
80
73
|
return {
|
|
81
74
|
node,
|
|
@@ -158,7 +151,7 @@ const create = context => {
|
|
|
158
151
|
replacement,
|
|
159
152
|
},
|
|
160
153
|
* fix(fixer) {
|
|
161
|
-
const
|
|
154
|
+
const isNeedsReturnOrThrowParentheses = (
|
|
162
155
|
(parent.type === 'ReturnStatement' || parent.type === 'ThrowStatement')
|
|
163
156
|
&& parent.argument === unaryExpression
|
|
164
157
|
&& !isOnSameLine(bangToken, tokenAfterBang, context)
|
|
@@ -171,12 +164,12 @@ const create = context => {
|
|
|
171
164
|
|
|
172
165
|
if (
|
|
173
166
|
tokenStore === sourceCode
|
|
174
|
-
&& !
|
|
167
|
+
&& !isNeedsReturnOrThrowParentheses
|
|
175
168
|
) {
|
|
176
169
|
yield fixSpaceAroundKeyword(fixer, unaryExpression, context);
|
|
177
170
|
}
|
|
178
171
|
|
|
179
|
-
if (
|
|
172
|
+
if (isNeedsReturnOrThrowParentheses) {
|
|
180
173
|
yield addParenthesesToReturnOrThrowExpression(fixer, parent, context);
|
|
181
174
|
return;
|
|
182
175
|
}
|
|
@@ -98,14 +98,14 @@ function * fix({
|
|
|
98
98
|
const tokenAfterBangIncludingComments = sourceCode.getTokenAfter(bangToken, {includeComments: true});
|
|
99
99
|
const operatorToken = getPunctuatorBinaryExpressionOperatorToken(comparison, context);
|
|
100
100
|
const {parent} = unaryExpression;
|
|
101
|
-
const
|
|
101
|
+
const isNeedsReturnOrThrowParentheses = (
|
|
102
102
|
(parent.type === 'ReturnStatement' || parent.type === 'ThrowStatement')
|
|
103
103
|
&& parent.argument === unaryExpression
|
|
104
104
|
&& !isOnSameLine(bangToken, tokenAfterBang, context)
|
|
105
105
|
&& !isParenthesized(unaryExpression, context)
|
|
106
106
|
);
|
|
107
107
|
|
|
108
|
-
if (!
|
|
108
|
+
if (!isNeedsReturnOrThrowParentheses) {
|
|
109
109
|
yield fixSpaceAroundKeyword(fixer, unaryExpression, context);
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -117,16 +117,13 @@ function * fix({
|
|
|
117
117
|
yield fixer.insertTextAfter(tokenAfterBangIncludingComments, ' ');
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
if (!(
|
|
121
|
-
parentNeedsGroupedComparison(parent)
|
|
122
|
-
&& !isParenthesized(unaryExpression, context)
|
|
123
|
-
)) {
|
|
120
|
+
if (!parentNeedsGroupedComparison(parent) || isParenthesized(unaryExpression, context)) {
|
|
124
121
|
yield removeParentheses(comparison, fixer, context);
|
|
125
122
|
}
|
|
126
123
|
|
|
127
124
|
yield fixer.replaceText(operatorToken, replacementOperator);
|
|
128
125
|
|
|
129
|
-
if (
|
|
126
|
+
if (isNeedsReturnOrThrowParentheses) {
|
|
130
127
|
yield addParenthesesToReturnOrThrowExpression(fixer, parent, context);
|
|
131
128
|
return;
|
|
132
129
|
}
|
|
@@ -178,21 +175,21 @@ function * fixLogical({
|
|
|
178
175
|
const bangToken = sourceCode.getFirstToken(unaryExpression);
|
|
179
176
|
const tokenAfterBang = sourceCode.getTokenAfter(bangToken);
|
|
180
177
|
const {parent} = unaryExpression;
|
|
181
|
-
const
|
|
178
|
+
const isNeedsReturnOrThrowParentheses = (
|
|
182
179
|
(parent.type === 'ReturnStatement' || parent.type === 'ThrowStatement')
|
|
183
180
|
&& parent.argument === unaryExpression
|
|
184
181
|
&& !isOnSameLine(bangToken, tokenAfterBang, context)
|
|
185
182
|
&& !isParenthesized(unaryExpression, context)
|
|
186
183
|
);
|
|
187
184
|
|
|
188
|
-
if (!
|
|
185
|
+
if (!isNeedsReturnOrThrowParentheses) {
|
|
189
186
|
yield fixSpaceAroundKeyword(fixer, unaryExpression, context);
|
|
190
187
|
}
|
|
191
188
|
|
|
192
189
|
yield fixer.remove(bangToken);
|
|
193
190
|
yield fixer.replaceText(logicalExpression, getFixedLogicalExpressionText(logicalExpression, context));
|
|
194
191
|
|
|
195
|
-
if (
|
|
192
|
+
if (isNeedsReturnOrThrowParentheses) {
|
|
196
193
|
yield addParenthesesToReturnOrThrowExpression(fixer, parent, context);
|
|
197
194
|
return;
|
|
198
195
|
}
|
|
@@ -23,11 +23,8 @@ const create = context => {
|
|
|
23
23
|
context.on('BinaryExpression', binaryExpression => {
|
|
24
24
|
const {operator, left} = binaryExpression;
|
|
25
25
|
|
|
26
|
-
if (!(
|
|
27
|
-
|
|
28
|
-
&& isNegatedExpression(left)
|
|
29
|
-
&& !isNegatedExpression(left.argument)
|
|
30
|
-
)) {
|
|
26
|
+
if (!(isEqualityCheck(binaryExpression)
|
|
27
|
+
&& isNegatedExpression(left)) || isNegatedExpression(left.argument)) {
|
|
31
28
|
return;
|
|
32
29
|
}
|
|
33
30
|
|
package/rules/no-process-exit.js
CHANGED
|
@@ -11,9 +11,9 @@ const isWorkerThreads = node =>
|
|
|
11
11
|
|
|
12
12
|
/** @param {import('eslint').Rule.RuleContext} context */
|
|
13
13
|
const create = context => {
|
|
14
|
-
const
|
|
14
|
+
const isStartsWithHashBang = context.sourceCode.lines[0].indexOf('#!') === 0;
|
|
15
15
|
|
|
16
|
-
if (
|
|
16
|
+
if (isStartsWithHashBang) {
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -22,7 +22,7 @@ const create = context => {
|
|
|
22
22
|
let processEventHandlerDepth = 0;
|
|
23
23
|
|
|
24
24
|
// Only report if it's outside a worker thread context. See #328.
|
|
25
|
-
let
|
|
25
|
+
let isRequiredWorkerThreadsModule = false;
|
|
26
26
|
const problemNodes = [];
|
|
27
27
|
|
|
28
28
|
const isProcessEventHandler = node => isMethodCall(node, {
|
|
@@ -39,7 +39,7 @@ const create = context => {
|
|
|
39
39
|
isStaticRequire(callExpression)
|
|
40
40
|
&& isWorkerThreads(callExpression.arguments[0])
|
|
41
41
|
) {
|
|
42
|
-
|
|
42
|
+
isRequiredWorkerThreadsModule = true;
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
45
|
|
|
@@ -49,7 +49,7 @@ const create = context => {
|
|
|
49
49
|
importDeclaration.source.type === 'Literal'
|
|
50
50
|
&& isWorkerThreads(importDeclaration.source)
|
|
51
51
|
) {
|
|
52
|
-
|
|
52
|
+
isRequiredWorkerThreadsModule = true;
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
55
|
|
|
@@ -81,7 +81,7 @@ const create = context => {
|
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
context.onExit('Program', function * () {
|
|
84
|
-
if (
|
|
84
|
+
if (isRequiredWorkerThreadsModule) {
|
|
85
85
|
return;
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -38,16 +38,10 @@ function isStaticMember(node) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
// TypeScript class
|
|
41
|
-
|
|
42
|
-
isDeclare
|
|
41
|
+
return !(isDeclare
|
|
43
42
|
|| isReadonly
|
|
44
43
|
|| accessibility !== undefined
|
|
45
|
-
|| (Array.isArray(decorators) && decorators.length > 0)
|
|
46
|
-
) {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return true;
|
|
44
|
+
|| (Array.isArray(decorators) && decorators.length > 0));
|
|
51
45
|
}
|
|
52
46
|
|
|
53
47
|
function * switchClassMemberToObjectProperty(node, context, fixer) {
|
package/rules/no-thenable.js
CHANGED
|
@@ -79,16 +79,13 @@ const cases = [
|
|
|
79
79
|
{
|
|
80
80
|
selector: 'CallExpression',
|
|
81
81
|
* getNodes(node, context) {
|
|
82
|
-
if (!(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
})
|
|
90
|
-
&& node.arguments[0].type !== 'SpreadElement'
|
|
91
|
-
)) {
|
|
82
|
+
if (!isMethodCall(node, {
|
|
83
|
+
objects: ['Object', 'Reflect'],
|
|
84
|
+
method: 'defineProperty',
|
|
85
|
+
minimumArguments: 3,
|
|
86
|
+
optionalCall: false,
|
|
87
|
+
optionalMember: false,
|
|
88
|
+
}) || node.arguments[0].type === 'SpreadElement') {
|
|
92
89
|
return;
|
|
93
90
|
}
|
|
94
91
|
|
|
@@ -117,11 +114,8 @@ const cases = [
|
|
|
117
114
|
}
|
|
118
115
|
|
|
119
116
|
for (const pairs of node.arguments[0].elements) {
|
|
120
|
-
if (!(
|
|
121
|
-
pairs
|
|
122
|
-
&& pairs.elements[0]
|
|
123
|
-
&& pairs.elements[0].type !== 'SpreadElement'
|
|
124
|
-
)) {
|
|
117
|
+
if (!(pairs?.type === 'ArrayExpression'
|
|
118
|
+
&& pairs.elements[0]) || pairs.elements[0].type === 'SpreadElement') {
|
|
125
119
|
continue;
|
|
126
120
|
}
|
|
127
121
|
|
|
@@ -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
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {hasSideEffect} from '@eslint-community/eslint-utils';
|
|
2
|
-
import {isMethodCall} from './ast/index.js';
|
|
2
|
+
import {isEmptyArrayExpression, isMethodCall} from './ast/index.js';
|
|
3
3
|
import {
|
|
4
4
|
getParenthesizedText,
|
|
5
5
|
isKnownNonArray,
|
|
6
6
|
isParenthesized,
|
|
7
7
|
isSameIdentifier,
|
|
8
|
+
isTypeScriptFile,
|
|
8
9
|
shouldAddParenthesesToMemberExpressionObject,
|
|
9
10
|
wouldRemoveComments,
|
|
10
11
|
} from './utils/index.js';
|
|
@@ -49,10 +50,6 @@ const isSimpleSingleParameterArrowCallback = node =>
|
|
|
49
50
|
&& !node.params[0].optional
|
|
50
51
|
&& node.body.type !== 'BlockStatement';
|
|
51
52
|
|
|
52
|
-
const isEmptyArrayExpression = node =>
|
|
53
|
-
node.type === 'ArrayExpression'
|
|
54
|
-
&& node.elements.length === 0;
|
|
55
|
-
|
|
56
53
|
const getSingleArrayElement = node => {
|
|
57
54
|
if (
|
|
58
55
|
node.type !== 'ArrayExpression'
|
|
@@ -195,6 +192,10 @@ function getProblemForFilterFlatMap(flatMapCallExpression, callbackResult, conte
|
|
|
195
192
|
}
|
|
196
193
|
|
|
197
194
|
function getProblemForConditionalFlatMap(flatMapCallExpression, callback, callbackResult, context) {
|
|
195
|
+
if (isTypeScriptFile(context.physicalFilename)) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
198
199
|
const method = isSameIdentifier(callbackResult.element, callback.params[0]) ? 'filter' : 'filter().map';
|
|
199
200
|
const problem = {
|
|
200
201
|
node: flatMapCallExpression.callee.property,
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
isIdentifierName,
|
|
3
|
-
isKeyword,
|
|
4
|
-
isReservedWord,
|
|
5
|
-
isStrictReservedWord,
|
|
6
|
-
} from '@babel/helper-validator-identifier';
|
|
1
|
+
import reservedIdentifiers from 'reserved-identifiers';
|
|
7
2
|
import {findVariable} from '@eslint-community/eslint-utils';
|
|
8
3
|
import {isStringLiteral} from './ast/index.js';
|
|
9
4
|
import {
|
|
@@ -12,6 +7,7 @@ import {
|
|
|
12
7
|
isLeftHandSide,
|
|
13
8
|
isControlFlowTest,
|
|
14
9
|
isBooleanExpression,
|
|
10
|
+
isIdentifierName,
|
|
15
11
|
} from './utils/index.js';
|
|
16
12
|
|
|
17
13
|
const MESSAGE_ID = 'no-unnecessary-global-this';
|
|
@@ -53,11 +49,11 @@ function isActiveGlobal(name, node, context) {
|
|
|
53
49
|
&& variable.defs.length === 0;
|
|
54
50
|
}
|
|
55
51
|
|
|
52
|
+
const reserved = reservedIdentifiers();
|
|
53
|
+
|
|
56
54
|
function canUseBareIdentifier(name) {
|
|
57
55
|
return isIdentifierName(name)
|
|
58
|
-
&& !
|
|
59
|
-
&& !isReservedWord(name, true)
|
|
60
|
-
&& !isStrictReservedWord(name, true);
|
|
56
|
+
&& (!reserved.has(name) || name === 'eval' || name === 'arguments');
|
|
61
57
|
}
|
|
62
58
|
|
|
63
59
|
function getStaticPropertyName(node) {
|
|
@@ -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;
|
|
@@ -410,10 +410,10 @@ const create = context => {
|
|
|
410
410
|
return;
|
|
411
411
|
}
|
|
412
412
|
|
|
413
|
-
const
|
|
413
|
+
const isAccessWhenTestPasses = !isConsequentUndefined;
|
|
414
414
|
const testValidity = getIndexedAccessTestValidity(node.test, indexedAccess);
|
|
415
415
|
|
|
416
|
-
if (testValidity !==
|
|
416
|
+
if (testValidity !== isAccessWhenTestPasses) {
|
|
417
417
|
return;
|
|
418
418
|
}
|
|
419
419
|
|
|
@@ -204,13 +204,10 @@ const create = context => {
|
|
|
204
204
|
// `array.filter()[0]`
|
|
205
205
|
// `array?.filter()[0]`
|
|
206
206
|
context.on('MemberExpression', node => {
|
|
207
|
-
if (!(
|
|
208
|
-
node.computed
|
|
207
|
+
if (!(node.computed
|
|
209
208
|
&& node.property.type === 'Literal'
|
|
210
209
|
&& node.property.raw === '0'
|
|
211
|
-
&& isArrayFilterCall(node.object, context)
|
|
212
|
-
&& !isLeftHandSide(node)
|
|
213
|
-
)) {
|
|
210
|
+
&& isArrayFilterCall(node.object, context)) || isLeftHandSide(node)) {
|
|
214
211
|
return;
|
|
215
212
|
}
|
|
216
213
|
|
|
@@ -75,6 +75,7 @@ function getReplacement(valueNode) {
|
|
|
75
75
|
const {value, raw} = valueNode;
|
|
76
76
|
|
|
77
77
|
// Intentionally `Number.isInteger`, not `Number.isSafeInteger`: large integers beyond the safe range are exactly what bigint literals are for.
|
|
78
|
+
// eslint-disable-next-line unicorn/prefer-number-is-safe-integer
|
|
78
79
|
if (!Number.isInteger(value)) {
|
|
79
80
|
return;
|
|
80
81
|
}
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
getLastTrailingCommentOnSameLine,
|
|
4
4
|
getNextNode,
|
|
5
5
|
getParenthesizedText,
|
|
6
|
+
getPreviousNode,
|
|
6
7
|
isGlobalBooleanCall,
|
|
7
8
|
shouldAddParenthesesToUnaryExpressionArgument,
|
|
8
9
|
} from './utils/index.js';
|
|
@@ -143,11 +144,11 @@ const create = context => {
|
|
|
143
144
|
const hasComments = sourceCode.getCommentsInside(node).length > 0
|
|
144
145
|
|| sourceCode.getCommentsInside(alternate).length > 0
|
|
145
146
|
|| getLastTrailingCommentOnSameLine(context, alternate);
|
|
146
|
-
const
|
|
147
|
+
const isNeedsGlobalBoolean = !shouldNegateTest
|
|
147
148
|
&& !isRuntimeBooleanExpression(node.test, context)
|
|
148
149
|
&& !canUseGlobalBoolean(node);
|
|
149
150
|
|
|
150
|
-
if (hasComments ||
|
|
151
|
+
if (hasComments || isNeedsGlobalBoolean) {
|
|
151
152
|
return problem;
|
|
152
153
|
}
|
|
153
154
|
|
|
@@ -160,6 +161,13 @@ const create = context => {
|
|
|
160
161
|
};
|
|
161
162
|
}
|
|
162
163
|
|
|
164
|
+
function isPrecededByFlatIfReturningSameBoolean(node, returnValue) {
|
|
165
|
+
const previousNode = getPreviousNode(node, context);
|
|
166
|
+
return previousNode?.type === 'IfStatement'
|
|
167
|
+
&& !previousNode.alternate
|
|
168
|
+
&& getBooleanReturnValue(getNodeBody(previousNode.consequent)) === returnValue;
|
|
169
|
+
}
|
|
170
|
+
|
|
163
171
|
function getFlatProblem(node) {
|
|
164
172
|
const alternate = getNextNode(node, context);
|
|
165
173
|
if (
|
|
@@ -169,9 +177,18 @@ const create = context => {
|
|
|
169
177
|
return;
|
|
170
178
|
}
|
|
171
179
|
|
|
180
|
+
const consequent = getNodeBody(node.consequent);
|
|
181
|
+
const consequentValue = getBooleanReturnValue(consequent);
|
|
182
|
+
if (
|
|
183
|
+
consequentValue !== undefined
|
|
184
|
+
&& isPrecededByFlatIfReturningSameBoolean(node, consequentValue)
|
|
185
|
+
) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
172
189
|
const problem = getProblem(
|
|
173
190
|
node,
|
|
174
|
-
|
|
191
|
+
consequent,
|
|
175
192
|
alternate,
|
|
176
193
|
[
|
|
177
194
|
sourceCode.getRange(node)[0],
|
|
@@ -108,8 +108,8 @@ const fixDefaultExpression = (fixer, sourceCode, node) => {
|
|
|
108
108
|
]);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
const
|
|
112
|
-
if (
|
|
111
|
+
const isEndsWithWhitespace = lineText[column] === ' ';
|
|
112
|
+
if (isEndsWithWhitespace) {
|
|
113
113
|
const [start, end] = sourceCode.getRange(node);
|
|
114
114
|
return fixer.removeRange([start, end + 1]);
|
|
115
115
|
}
|
package/rules/prefer-else-if.js
CHANGED
|
@@ -148,13 +148,13 @@ function getEqualityComparisons(node) {
|
|
|
148
148
|
*/
|
|
149
149
|
function getBooleanComparisonInfo(node, context) {
|
|
150
150
|
node = unwrapExpression(node);
|
|
151
|
-
let
|
|
151
|
+
let isValue = true;
|
|
152
152
|
|
|
153
153
|
while (
|
|
154
154
|
node.type === 'UnaryExpression'
|
|
155
155
|
&& node.operator === '!'
|
|
156
156
|
) {
|
|
157
|
-
|
|
157
|
+
isValue = !isValue;
|
|
158
158
|
node = unwrapExpression(node.argument);
|
|
159
159
|
}
|
|
160
160
|
|
|
@@ -178,7 +178,7 @@ function getBooleanComparisonInfo(node, context) {
|
|
|
178
178
|
|
|
179
179
|
return {
|
|
180
180
|
discriminant: node,
|
|
181
|
-
valueKeys: new Set([getStaticEqualityValueKey(
|
|
181
|
+
valueKeys: new Set([getStaticEqualityValueKey(isValue)]),
|
|
182
182
|
};
|
|
183
183
|
}
|
|
184
184
|
|
|
@@ -444,13 +444,9 @@ function getProblem(previousIfStatement, ifStatement, context, branchAlwaysExits
|
|
|
444
444
|
const previous = getComparisonInfo(previousIfStatement.test, context);
|
|
445
445
|
const current = getComparisonInfo(ifStatement.test, context);
|
|
446
446
|
|
|
447
|
-
if (!(
|
|
448
|
-
previous
|
|
447
|
+
if (!(previous
|
|
449
448
|
&& current
|
|
450
|
-
&& isSame(previous.discriminant, current.discriminant)
|
|
451
|
-
&& !hasOverlappingValues(previous.valueKeys, current.valueKeys)
|
|
452
|
-
&& !hasDirectDiscriminantMutation(previousIfStatement.consequent, previous.discriminant, context)
|
|
453
|
-
)) {
|
|
449
|
+
&& isSame(previous.discriminant, current.discriminant)) || hasOverlappingValues(previous.valueKeys, current.valueKeys) || hasDirectDiscriminantMutation(previousIfStatement.consequent, previous.discriminant, context)) {
|
|
454
450
|
return;
|
|
455
451
|
}
|
|
456
452
|
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import {findVariable, getStaticValue} from '@eslint-community/eslint-utils';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
isMethodCall,
|
|
4
|
+
isNewExpression,
|
|
5
|
+
isNullLiteral,
|
|
6
|
+
isUndefined,
|
|
7
|
+
} from './ast/index.js';
|
|
3
8
|
import {
|
|
4
9
|
getParenthesizedText,
|
|
5
10
|
getTypeSymbol,
|
|
@@ -823,8 +828,6 @@ const getCallKind = (callExpression, comparison, context) => {
|
|
|
823
828
|
}
|
|
824
829
|
};
|
|
825
830
|
|
|
826
|
-
const isNullLiteral = node => node.type === 'Literal' && node.value === null;
|
|
827
|
-
|
|
828
831
|
const getComparison = callExpression => {
|
|
829
832
|
const comparisonTarget = getTransparentExpressionAncestor(callExpression);
|
|
830
833
|
const {parent} = comparisonTarget;
|
package/rules/prefer-https.js
CHANGED
|
@@ -82,14 +82,14 @@ function isWellKnownXmlNamespace(text, matchIndex) {
|
|
|
82
82
|
|
|
83
83
|
/** @param {import('eslint').Rule.RuleContext} context */
|
|
84
84
|
const create = context => {
|
|
85
|
-
let
|
|
85
|
+
let isChecked = false;
|
|
86
86
|
|
|
87
87
|
onRoot(context, node => {
|
|
88
|
-
if (
|
|
88
|
+
if (isChecked) {
|
|
89
89
|
return;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
isChecked = true;
|
|
93
93
|
|
|
94
94
|
const {sourceCode} = context;
|
|
95
95
|
const {text} = sourceCode;
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
const {isIdentifierName} = helperValidatorIdentifier;
|
|
1
|
+
import {isIdentifierName} from './utils/index.js';
|
|
4
2
|
|
|
5
3
|
const MESSAGE_ID = 'prefer-identifier-import-export-specifiers';
|
|
6
4
|
const messages = {
|
|
@@ -166,14 +166,14 @@ function isMinimalBinaryExpression(left, right, context) {
|
|
|
166
166
|
return false;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
const
|
|
170
|
-
const
|
|
169
|
+
const isLeftSidesAreSame = isSameSourceText(left.left, right.left, context.sourceCode);
|
|
170
|
+
const isRightSidesAreSame = isSameSourceText(left.right, right.right, context.sourceCode);
|
|
171
171
|
|
|
172
|
-
if (
|
|
172
|
+
if (isLeftSidesAreSame === isRightSidesAreSame) {
|
|
173
173
|
return false;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
return
|
|
176
|
+
return isRightSidesAreSame || isSafeSharedExpression(left.left);
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
function hasSameObjectWithDifferentDynamicKey(left, right, context) {
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import {getStaticValue} from '@eslint-community/eslint-utils';
|
|
2
|
-
import helperValidatorIdentifier from '@babel/helper-validator-identifier';
|
|
3
2
|
import {isMethodCall} from './ast/index.js';
|
|
4
3
|
import {
|
|
5
4
|
getIndentString,
|
|
6
5
|
getNextNode,
|
|
7
6
|
getPreviousNode,
|
|
8
7
|
isSameReference,
|
|
8
|
+
isIdentifierName,
|
|
9
9
|
} from './utils/index.js';
|
|
10
10
|
|
|
11
|
-
const {isIdentifierName} = helperValidatorIdentifier;
|
|
12
|
-
|
|
13
11
|
const MESSAGE_ID = 'prefer-object-define-properties';
|
|
14
12
|
const messages = {
|
|
15
13
|
[MESSAGE_ID]: 'Prefer `Object.defineProperties()` over multiple `Object.defineProperty()` calls.',
|
|
@@ -231,11 +231,8 @@ const isGlobalObject = (node, context) =>
|
|
|
231
231
|
const isWriteOnlyLeftHandSide = node =>
|
|
232
232
|
isLeftHandSide(node)
|
|
233
233
|
&& node.parent.type !== 'UpdateExpression'
|
|
234
|
-
&& !(
|
|
235
|
-
node.parent.
|
|
236
|
-
&& node.parent.left === node
|
|
237
|
-
&& node.parent.operator !== '='
|
|
238
|
-
);
|
|
234
|
+
&& (!(node.parent.type === 'AssignmentExpression'
|
|
235
|
+
&& node.parent.left === node) || node.parent.operator === '=');
|
|
239
236
|
|
|
240
237
|
const isGlobalDocument = (node, context) => {
|
|
241
238
|
node = unwrapTypeScriptExpression(node);
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import helperValidatorIdentifier from '@babel/helper-validator-identifier';
|
|
2
1
|
import {getStaticStringValue} from './ast/index.js';
|
|
3
|
-
|
|
4
|
-
const {isIdentifierName} = helperValidatorIdentifier;
|
|
2
|
+
import {isIdentifierName} from './utils/index.js';
|
|
5
3
|
|
|
6
4
|
const MESSAGE_ID = 'prefer-private-class-fields';
|
|
7
5
|
const messages = {
|
|
@@ -138,14 +138,14 @@ function trueBranchSign(branch) {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
function expectedSignForTest(test, minuend, subtrahend) {
|
|
141
|
-
const
|
|
141
|
+
const isGreaterThan = test.operator === '>' || test.operator === '>=';
|
|
142
142
|
const testLeftIsMinuend = isSameReference(test.left, minuend) && isSameReference(test.right, subtrahend);
|
|
143
143
|
|
|
144
144
|
if (testLeftIsMinuend) {
|
|
145
|
-
return
|
|
145
|
+
return isGreaterThan ? 1 : -1;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
return
|
|
148
|
+
return isGreaterThan ? -1 : 1;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
function branchSignsMatchSubtraction(branch, minuend, subtrahend) {
|
|
@@ -220,9 +220,9 @@ const create = context => {
|
|
|
220
220
|
|
|
221
221
|
// Orient the subtraction so it matches the comparator's sort direction.
|
|
222
222
|
const {test} = tree;
|
|
223
|
-
const
|
|
224
|
-
const
|
|
225
|
-
const [minuend, subtrahend] =
|
|
223
|
+
const isGreaterThan = test.operator === '>' || test.operator === '>=';
|
|
224
|
+
const isLeftIsMinuend = isGreaterThan ? sign > 0 : sign < 0;
|
|
225
|
+
const [minuend, subtrahend] = isLeftIsMinuend ? [test.left, test.right] : [test.right, test.left];
|
|
226
226
|
|
|
227
227
|
if (!branchSignsMatchSubtraction(tree, minuend, subtrahend)) {
|
|
228
228
|
return;
|
|
@@ -68,15 +68,12 @@ const getProblem = (sourceCode, firstNode, secondNode) => {
|
|
|
68
68
|
const first = getSupportedDeclaration(sourceCode, firstNode);
|
|
69
69
|
const second = getSupportedDeclaration(sourceCode, secondNode);
|
|
70
70
|
|
|
71
|
-
if (!(
|
|
72
|
-
first
|
|
71
|
+
if (!(first
|
|
73
72
|
&& second
|
|
74
73
|
&& first.node.kind === second.node.kind
|
|
75
74
|
&& first.source === second.source
|
|
76
75
|
&& sourceCode.getCommentsInside(first.node).length === 0
|
|
77
|
-
&& sourceCode.getCommentsInside(second.node).length === 0
|
|
78
|
-
&& !hasCommentsBetween(sourceCode, first.node, second.node)
|
|
79
|
-
)) {
|
|
76
|
+
&& sourceCode.getCommentsInside(second.node).length === 0) || hasCommentsBetween(sourceCode, first.node, second.node)) {
|
|
80
77
|
return;
|
|
81
78
|
}
|
|
82
79
|
|
|
@@ -21,8 +21,8 @@ const characterClassEscapes = new Map([
|
|
|
21
21
|
['\t', String.raw`\t`],
|
|
22
22
|
['\f', String.raw`\f`],
|
|
23
23
|
['\v', String.raw`\v`],
|
|
24
|
-
['\
|
|
25
|
-
['\
|
|
24
|
+
['\u{2028}', String.raw`\u2028`],
|
|
25
|
+
['\u{2029}', String.raw`\u2029`],
|
|
26
26
|
]);
|
|
27
27
|
|
|
28
28
|
// Escapes a single character for use inside a character class. `^` and `-` are only special depending on position, so they are escaped only when necessary (avoids tripping `no-useless-escape` on the output).
|