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
|
@@ -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).
|
package/rules/prefer-spread.js
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
isTypeParameterType,
|
|
19
19
|
hasUnparenthesizedOptionalChainElement,
|
|
20
20
|
isTypeScriptExpressionWrapper,
|
|
21
|
+
isDefaultLibrarySymbol,
|
|
21
22
|
} from './utils/index.js';
|
|
22
23
|
import {removeMethodCall} from './fix/index.js';
|
|
23
24
|
import {
|
|
@@ -155,8 +156,8 @@ function fixConcat(node, context, fixableArguments) {
|
|
|
155
156
|
const array = node.callee.object;
|
|
156
157
|
const concatCallArguments = node.arguments;
|
|
157
158
|
const arrayParenthesizedRange = getParenthesizedRange(array, context);
|
|
158
|
-
const
|
|
159
|
-
const arrayHasTrailingComma =
|
|
159
|
+
const isArrayIsArrayLiteral = isArrayLiteral(array);
|
|
160
|
+
const arrayHasTrailingComma = isArrayIsArrayLiteral && isArrayLiteralHasTrailingComma(array, sourceCode);
|
|
160
161
|
|
|
161
162
|
const getArrayLiteralElementsText = (node, keepTrailingComma) => {
|
|
162
163
|
if (
|
|
@@ -204,7 +205,7 @@ function fixConcat(node, context, fixableArguments) {
|
|
|
204
205
|
return '';
|
|
205
206
|
}
|
|
206
207
|
|
|
207
|
-
if (
|
|
208
|
+
if (isArrayIsArrayLiteral) {
|
|
208
209
|
if (!isEmptyArrayExpression(array)) {
|
|
209
210
|
text = ` ${text}`;
|
|
210
211
|
|
|
@@ -243,7 +244,7 @@ function fixConcat(node, context, fixableArguments) {
|
|
|
243
244
|
return function * (fixer) {
|
|
244
245
|
// Fixed code always starts with `[`
|
|
245
246
|
if (
|
|
246
|
-
!
|
|
247
|
+
!isArrayIsArrayLiteral
|
|
247
248
|
&& needsSemicolon(sourceCode.getTokenBefore(node), context, '[')
|
|
248
249
|
) {
|
|
249
250
|
yield fixer.insertTextBefore(node, ';');
|
|
@@ -257,7 +258,7 @@ function fixConcat(node, context, fixableArguments) {
|
|
|
257
258
|
|
|
258
259
|
const text = getFixedText();
|
|
259
260
|
|
|
260
|
-
if (
|
|
261
|
+
if (isArrayIsArrayLiteral) {
|
|
261
262
|
const closingBracketToken = sourceCode.getLastToken(array);
|
|
262
263
|
yield fixer.insertTextBefore(closingBracketToken, text);
|
|
263
264
|
} else {
|
|
@@ -389,9 +390,6 @@ function isGlobalMemberExpression(node, objectName, propertyName, context) {
|
|
|
389
390
|
return staticValue?.value === propertyName;
|
|
390
391
|
}
|
|
391
392
|
|
|
392
|
-
const isDefaultLibrarySymbol = (symbol, program) =>
|
|
393
|
-
symbol?.declarations?.some(declaration => program.isSourceFileDefaultLibrary(declaration.getSourceFile())) ?? false;
|
|
394
|
-
|
|
395
393
|
function isBufferModuleImport(definition) {
|
|
396
394
|
return (
|
|
397
395
|
definition?.type === 'ImportBinding'
|
|
@@ -1339,11 +1337,11 @@ const create = context => {
|
|
|
1339
1337
|
};
|
|
1340
1338
|
|
|
1341
1339
|
const fixableArguments = getConcatFixableArguments(node.arguments, scope, context);
|
|
1342
|
-
const
|
|
1340
|
+
const isReceiverSafeToSpread = !isArrayConstructorWithOneArgument(object, context);
|
|
1343
1341
|
|
|
1344
1342
|
if (fixableArguments.length > 0 || node.arguments.length === 0) {
|
|
1345
1343
|
if (
|
|
1346
|
-
|
|
1344
|
+
isReceiverSafeToSpread
|
|
1347
1345
|
&& !hasCommentsOutsideRanges(node, getConcatPreservedRanges(node, fixableArguments.length))
|
|
1348
1346
|
) {
|
|
1349
1347
|
problem.fix = fixConcat(node, context, fixableArguments);
|
|
@@ -1357,7 +1355,7 @@ const create = context => {
|
|
|
1357
1355
|
return problem;
|
|
1358
1356
|
}
|
|
1359
1357
|
|
|
1360
|
-
if (!
|
|
1358
|
+
if (!isReceiverSafeToSpread) {
|
|
1361
1359
|
return problem;
|
|
1362
1360
|
}
|
|
1363
1361
|
|
|
@@ -1422,17 +1420,13 @@ const create = context => {
|
|
|
1422
1420
|
|
|
1423
1421
|
// `array.slice()`
|
|
1424
1422
|
context.on('CallExpression', node => {
|
|
1425
|
-
if (!(
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
})
|
|
1433
|
-
&& !isArrayLiteral(node.callee.object)
|
|
1434
|
-
&& !hasUnparenthesizedOptionalChainElement(node.callee.object, context)
|
|
1435
|
-
)) {
|
|
1423
|
+
if (!isMethodCall(node, {
|
|
1424
|
+
method: 'slice',
|
|
1425
|
+
minimumArguments: 0,
|
|
1426
|
+
maximumArguments: 1,
|
|
1427
|
+
optionalCall: false,
|
|
1428
|
+
optionalMember: false,
|
|
1429
|
+
}) || isArrayLiteral(node.callee.object) || hasUnparenthesizedOptionalChainElement(node.callee.object, context)) {
|
|
1436
1430
|
return;
|
|
1437
1431
|
}
|
|
1438
1432
|
|
|
@@ -1465,16 +1459,12 @@ const create = context => {
|
|
|
1465
1459
|
|
|
1466
1460
|
// `array.toSpliced()`
|
|
1467
1461
|
context.on('CallExpression', node => {
|
|
1468
|
-
if (!(
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
})
|
|
1475
|
-
&& node.callee.object.type !== 'ArrayExpression'
|
|
1476
|
-
&& !hasUnparenthesizedOptionalChainElement(node.callee.object, context)
|
|
1477
|
-
)) {
|
|
1462
|
+
if (!isMethodCall(node, {
|
|
1463
|
+
method: 'toSpliced',
|
|
1464
|
+
argumentsLength: 0,
|
|
1465
|
+
optionalCall: false,
|
|
1466
|
+
optionalMember: false,
|
|
1467
|
+
}) || node.callee.object.type === 'ArrayExpression' || hasUnparenthesizedOptionalChainElement(node.callee.object, context)) {
|
|
1478
1468
|
return;
|
|
1479
1469
|
}
|
|
1480
1470
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {findVariable, getStaticValue} from '@eslint-community/eslint-utils';
|
|
2
|
-
import {isMethodCall} from './ast/index.js';
|
|
2
|
+
import {isMethodCall, isNumericLiteral} from './ast/index.js';
|
|
3
3
|
import {
|
|
4
4
|
getParenthesizedText,
|
|
5
5
|
isSameReference,
|
|
@@ -78,11 +78,6 @@ const hasCommentsInside = (node, context) => context.sourceCode.getCommentsInsid
|
|
|
78
78
|
|
|
79
79
|
const isSimpleTarget = node => node.type === 'Identifier';
|
|
80
80
|
|
|
81
|
-
const isNumericLiteral = node => (
|
|
82
|
-
node.type === 'Literal'
|
|
83
|
-
&& typeof node.value === 'number'
|
|
84
|
-
);
|
|
85
|
-
|
|
86
81
|
const isZeroLiteral = node => (
|
|
87
82
|
isNumericLiteral(node)
|
|
88
83
|
&& node.value === 0
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import jsesc from 'jsesc';
|
|
2
1
|
import {isStringLiteral, isDirective} from './ast/index.js';
|
|
3
2
|
import {fixSpaceAroundKeyword} from './fix/index.js';
|
|
4
3
|
import isJestInlineSnapshot from './shared/is-jest-inline-snapshot.js';
|
|
@@ -10,15 +9,23 @@ const messages = {
|
|
|
10
9
|
|
|
11
10
|
const singleWhitespace = /^\s$/u;
|
|
12
11
|
|
|
12
|
+
const namedEscapes = new Map([
|
|
13
|
+
['\t', String.raw`\t`],
|
|
14
|
+
['\n', String.raw`\n`],
|
|
15
|
+
['\r', String.raw`\r`],
|
|
16
|
+
['\f', String.raw`\f`],
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
// Escape a single whitespace character into a readable single-quoted string literal.
|
|
13
20
|
function quoteWhitespace(character) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
21
|
+
if (character === ' ') {
|
|
22
|
+
return '\' \'';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const escape = namedEscapes.get(character)
|
|
26
|
+
?? String.raw`\u{${character.codePointAt(0).toString(16).toUpperCase()}}`;
|
|
27
|
+
|
|
28
|
+
return `'${escape}'`;
|
|
22
29
|
}
|
|
23
30
|
|
|
24
31
|
function getRepeatedWhitespace(value, minimumRepetitions) {
|
|
@@ -40,7 +40,7 @@ function hasSafeGlobalStringReplacementFlags(flags) {
|
|
|
40
40
|
return true;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const getUnicodeEscape = codePoint => String.raw`\u{${codePoint.toString(16)}}`;
|
|
43
|
+
const getUnicodeEscape = codePoint => String.raw`\u{${codePoint.toString(16).toUpperCase()}}`;
|
|
44
44
|
|
|
45
45
|
function getValueReplacement(node) {
|
|
46
46
|
const {kind, codePoint, raw} = node;
|
package/rules/prefer-ternary.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {hasSideEffect, findVariable} from '@eslint-community/eslint-utils';
|
|
2
|
+
import {isBooleanLiteral} from './ast/index.js';
|
|
2
3
|
import {
|
|
3
4
|
needsSemicolon,
|
|
4
5
|
isSameReference,
|
|
@@ -13,7 +14,6 @@ const messageId = 'prefer-ternary';
|
|
|
13
14
|
const suggestionMessageId = 'prefer-ternary/suggestion';
|
|
14
15
|
|
|
15
16
|
const isTernary = node => node?.type === 'ConditionalExpression';
|
|
16
|
-
const isBooleanLiteral = node => node?.type === 'Literal' && typeof node.value === 'boolean';
|
|
17
17
|
|
|
18
18
|
function getNodeBody(node) {
|
|
19
19
|
/* c8 ignore next 3 */
|
|
@@ -57,7 +57,7 @@ const isMergeableAssignmentExpression = (consequent, alternate) =>
|
|
|
57
57
|
|
|
58
58
|
/** @param {import('eslint').Rule.RuleContext} context */
|
|
59
59
|
const create = context => {
|
|
60
|
-
const
|
|
60
|
+
const isOnlySingleLine = context.options[0] === 'only-single-line';
|
|
61
61
|
const {sourceCode} = context;
|
|
62
62
|
|
|
63
63
|
const getText = node => {
|
|
@@ -134,7 +134,7 @@ const create = context => {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
if (
|
|
137
|
-
|
|
137
|
+
isOnlySingleLine
|
|
138
138
|
&& [node.test, right].some(n => !isSingleLineNode(n, context))
|
|
139
139
|
) {
|
|
140
140
|
return;
|
|
@@ -161,7 +161,7 @@ const create = context => {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
if (
|
|
164
|
-
|
|
164
|
+
isOnlySingleLine
|
|
165
165
|
&& !isSingleLineNode(declarator.init, context)
|
|
166
166
|
) {
|
|
167
167
|
return;
|
|
@@ -245,7 +245,7 @@ const create = context => {
|
|
|
245
245
|
const alternate = getNodeBody(node.alternate);
|
|
246
246
|
|
|
247
247
|
if (
|
|
248
|
-
|
|
248
|
+
isOnlySingleLine
|
|
249
249
|
&& [consequent, alternate, node.test].some(node => !isSingleLineNode(node, context))
|
|
250
250
|
) {
|
|
251
251
|
return;
|