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
|
@@ -113,15 +113,12 @@ function isBufferEncoding(node, scope) {
|
|
|
113
113
|
|
|
114
114
|
function isJsonReadFileCall(node, scope) {
|
|
115
115
|
if (
|
|
116
|
-
!(
|
|
117
|
-
node
|
|
116
|
+
!(node
|
|
118
117
|
&& node.type === 'CallExpression'
|
|
119
118
|
&& !node.optional
|
|
120
119
|
&& (node.arguments.length === 1 || node.arguments.length === 2)
|
|
121
120
|
&& node.arguments.every(node => node.type !== 'SpreadElement')
|
|
122
|
-
&& node.callee.type === 'MemberExpression'
|
|
123
|
-
&& !node.callee.optional
|
|
124
|
-
)
|
|
121
|
+
&& node.callee.type === 'MemberExpression') || node.callee.optional
|
|
125
122
|
) {
|
|
126
123
|
return false;
|
|
127
124
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import helperValidatorIdentifier from '@babel/helper-validator-identifier';
|
|
2
1
|
import {findVariable} from '@eslint-community/eslint-utils';
|
|
3
2
|
import {
|
|
4
3
|
escapeString,
|
|
@@ -12,6 +11,7 @@ import {
|
|
|
12
11
|
needsSemicolon,
|
|
13
12
|
shouldAddParenthesesToMemberExpressionObject,
|
|
14
13
|
wouldRemoveComments,
|
|
14
|
+
isIdentifierName,
|
|
15
15
|
} from './utils/index.js';
|
|
16
16
|
import {removeStatement} from './fix/index.js';
|
|
17
17
|
import {
|
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
isExpressionStatement,
|
|
22
22
|
} from './ast/index.js';
|
|
23
23
|
|
|
24
|
-
const {isIdentifierName} = helperValidatorIdentifier;
|
|
25
24
|
const MESSAGE_ID = 'prefer-dataset';
|
|
26
25
|
const INVERSE_MESSAGE_ID = 'prefer-attributes';
|
|
27
26
|
const messages = {
|
|
@@ -106,11 +105,8 @@ function getDatasetVariableReadMembers(variable) {
|
|
|
106
105
|
|
|
107
106
|
const {identifier} = reference;
|
|
108
107
|
const member = identifier.parent;
|
|
109
|
-
if (!(
|
|
110
|
-
member.
|
|
111
|
-
&& member.object === identifier
|
|
112
|
-
&& !member.optional
|
|
113
|
-
)) {
|
|
108
|
+
if (!(member.type === 'MemberExpression'
|
|
109
|
+
&& member.object === identifier) || member.optional) {
|
|
114
110
|
return;
|
|
115
111
|
}
|
|
116
112
|
|
package/rules/escape-case.js
CHANGED
|
@@ -24,14 +24,14 @@ const getProblem = ({node, original, regex = escapeCase, lowercase, fix}) => {
|
|
|
24
24
|
|
|
25
25
|
/** @param {import('eslint').Rule.RuleContext} context */
|
|
26
26
|
const create = context => {
|
|
27
|
-
const
|
|
27
|
+
const isLowercase = context.options[0] === 'lowercase';
|
|
28
28
|
|
|
29
29
|
context.on('Literal', node => {
|
|
30
30
|
if (isStringLiteral(node)) {
|
|
31
31
|
return getProblem({
|
|
32
32
|
node,
|
|
33
33
|
original: node.raw,
|
|
34
|
-
lowercase,
|
|
34
|
+
lowercase: isLowercase,
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
});
|
|
@@ -42,7 +42,7 @@ const create = context => {
|
|
|
42
42
|
node,
|
|
43
43
|
original: node.raw,
|
|
44
44
|
regex: escapePatternCase,
|
|
45
|
-
lowercase,
|
|
45
|
+
lowercase: isLowercase,
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
});
|
|
@@ -55,7 +55,7 @@ const create = context => {
|
|
|
55
55
|
return getProblem({
|
|
56
56
|
node,
|
|
57
57
|
original: node.value.raw,
|
|
58
|
-
lowercase,
|
|
58
|
+
lowercase: isLowercase,
|
|
59
59
|
fix: (fixer, fixed) => replaceTemplateElement(node, fixed, context, fixer),
|
|
60
60
|
});
|
|
61
61
|
});
|
|
@@ -191,8 +191,8 @@ function getPackageHelpers(dirname) {
|
|
|
191
191
|
|
|
192
192
|
// Check if have to skip colon
|
|
193
193
|
// @example "TODO [...]: message here"
|
|
194
|
-
const
|
|
195
|
-
if (
|
|
194
|
+
const isDropColon = afterArguments[0] === ':';
|
|
195
|
+
if (isDropColon) {
|
|
196
196
|
return afterArguments.slice(1).trim();
|
|
197
197
|
}
|
|
198
198
|
|
|
@@ -250,7 +250,7 @@ function create(context) {
|
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
let node;
|
|
253
|
-
let
|
|
253
|
+
let isAutoFix = true;
|
|
254
254
|
let {isZeroLengthCheck, node: lengthCheckNode} = getLengthCheckNode(lengthNode);
|
|
255
255
|
if (lengthCheckNode) {
|
|
256
256
|
const {isNegative, node: ancestor} = getBooleanAncestor(lengthCheckNode, context);
|
|
@@ -266,7 +266,7 @@ function create(context) {
|
|
|
266
266
|
} else if (isLogicalExpression(lengthNode.parent) && lengthNode.parent.operator === '&&') {
|
|
267
267
|
isZeroLengthCheck = isNegative;
|
|
268
268
|
node = lengthNode;
|
|
269
|
-
|
|
269
|
+
isAutoFix = false;
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
272
|
|
|
@@ -287,7 +287,7 @@ function create(context) {
|
|
|
287
287
|
node,
|
|
288
288
|
isZeroLengthCheck,
|
|
289
289
|
lengthNode,
|
|
290
|
-
autoFix:
|
|
290
|
+
autoFix: isAutoFix && !isUnsafeNegationInBinaryExpression,
|
|
291
291
|
shouldSuggest: !isUnsafeNegationInBinaryExpression,
|
|
292
292
|
});
|
|
293
293
|
}
|
package/rules/filename-case.js
CHANGED
|
@@ -280,8 +280,8 @@ const create = context => {
|
|
|
280
280
|
|
|
281
281
|
return new RegExp(item, 'u');
|
|
282
282
|
});
|
|
283
|
-
const
|
|
284
|
-
const
|
|
283
|
+
const isMultipleFileExtensions = options.multipleFileExtensions !== false;
|
|
284
|
+
const isCheckDirectories = options.checkDirectories !== false;
|
|
285
285
|
const chosenCasesFunctions = chosenCases.map(case_ => cases[case_].fn);
|
|
286
286
|
|
|
287
287
|
onRoot(context, () => {
|
|
@@ -292,13 +292,13 @@ const create = context => {
|
|
|
292
292
|
filename,
|
|
293
293
|
middle,
|
|
294
294
|
extension,
|
|
295
|
-
} = getFilenameParts(basenameWithExtension, {multipleFileExtensions});
|
|
295
|
+
} = getFilenameParts(basenameWithExtension, {multipleFileExtensions: isMultipleFileExtensions});
|
|
296
296
|
|
|
297
297
|
if (pathSegments.some(segment => ignore.some(regexp => regexp.test(segment)))) {
|
|
298
298
|
return;
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
if (
|
|
301
|
+
if (isCheckDirectories) {
|
|
302
302
|
for (const directory of pathSegments.slice(0, -1)) {
|
|
303
303
|
const report = getInvalidDirectoryReport(directory, chosenCases, chosenCasesFunctions);
|
|
304
304
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {isCommaToken} from '@eslint-community/eslint-utils';
|
|
2
|
-
import {getParentheses} from '../utils/index.js';
|
|
2
|
+
import {getParentheses, hasCommentInRange} from '../utils/index.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
@import {TSESTree as ESTree} from '@typescript-eslint/types';
|
|
@@ -22,17 +22,22 @@ function getArgumentRemovalRange(node, context) {
|
|
|
22
22
|
let [start] = sourceCode.getRange(firstToken);
|
|
23
23
|
let [, end] = sourceCode.getRange(lastToken);
|
|
24
24
|
|
|
25
|
-
if (index !== 0) {
|
|
26
|
-
const commaToken = sourceCode.getTokenBefore(firstToken);
|
|
27
|
-
[start] = sourceCode.getRange(commaToken);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// If the removed argument is the only argument, the trailing comma must be removed too
|
|
31
25
|
if (callOrNewExpression.arguments.length === 1) {
|
|
26
|
+
// The only argument: also drop a dangling trailing comma if present (`fn(a,)`).
|
|
32
27
|
const tokenAfter = sourceCode.getTokenAfter(lastToken);
|
|
33
28
|
if (isCommaToken(tokenAfter)) {
|
|
34
29
|
[, end] = sourceCode.getRange(tokenAfter);
|
|
35
30
|
}
|
|
31
|
+
} else if (index === 0) {
|
|
32
|
+
// First of several: remove it through the following comma and the gap after it, so
|
|
33
|
+
// `fn(a, b)` becomes `fn(b)` rather than `fn( b)`.
|
|
34
|
+
const commaToken = sourceCode.getTokenAfter(lastToken);
|
|
35
|
+
const tokenAfterComma = sourceCode.getTokenAfter(commaToken, {includeComments: true});
|
|
36
|
+
[end] = sourceCode.getRange(tokenAfterComma);
|
|
37
|
+
} else {
|
|
38
|
+
// Otherwise remove the comma that precedes it.
|
|
39
|
+
const commaToken = sourceCode.getTokenBefore(firstToken);
|
|
40
|
+
[start] = sourceCode.getRange(commaToken);
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
return [start, end];
|
|
@@ -45,7 +50,28 @@ function getArgumentRemovalRange(node, context) {
|
|
|
45
50
|
@returns {ESLint.Rule.Fix}
|
|
46
51
|
*/
|
|
47
52
|
export default function removeArgument(fixer, node, context) {
|
|
48
|
-
|
|
53
|
+
const removalRange = getArgumentRemovalRange(node, context);
|
|
54
|
+
const callOrNewExpression = node.parent;
|
|
55
|
+
const index = callOrNewExpression.arguments.indexOf(node);
|
|
56
|
+
|
|
57
|
+
if (
|
|
58
|
+
index === 0
|
|
59
|
+
&& callOrNewExpression.arguments.length > 1
|
|
60
|
+
&& hasCommentInRange(context, removalRange)
|
|
61
|
+
) {
|
|
62
|
+
const {sourceCode} = context;
|
|
63
|
+
const parentheses = getParentheses(node, context);
|
|
64
|
+
const lastToken = parentheses.at(-1) || node;
|
|
65
|
+
const [, argumentEnd] = sourceCode.getRange(lastToken);
|
|
66
|
+
const commaToken = sourceCode.getTokenAfter(lastToken);
|
|
67
|
+
const [commaStart, commaEnd] = sourceCode.getRange(commaToken);
|
|
68
|
+
const rangeText = sourceCode.text.slice(...removalRange);
|
|
69
|
+
const replacement = rangeText.slice(argumentEnd - removalRange[0], commaStart - removalRange[0]) + rangeText.slice(commaEnd - removalRange[0]);
|
|
70
|
+
|
|
71
|
+
return fixer.replaceTextRange(removalRange, replacement);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return fixer.removeRange(removalRange);
|
|
49
75
|
}
|
|
50
76
|
|
|
51
77
|
export {
|
|
@@ -3,7 +3,7 @@ import {isSemicolonToken} from '@eslint-community/eslint-utils';
|
|
|
3
3
|
const isWhitespaceOnly = text => /^\s*$/.test(text);
|
|
4
4
|
|
|
5
5
|
// Removes a statement node along with its surrounding whitespace, while preserving comments.
|
|
6
|
-
function removeStatement(statement, context, fixer, preserveSemiColon = false) {
|
|
6
|
+
export default function removeStatement(statement, context, fixer, preserveSemiColon = false) {
|
|
7
7
|
const {sourceCode} = context;
|
|
8
8
|
const {lines} = sourceCode;
|
|
9
9
|
let endToken = statement;
|
|
@@ -44,5 +44,3 @@ function removeStatement(statement, context, fixer, preserveSemiColon = false) {
|
|
|
44
44
|
|
|
45
45
|
return fixer.removeRange([start, end]);
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
export default removeStatement;
|
package/rules/id-match.js
CHANGED
|
@@ -26,11 +26,11 @@ const shouldIgnoreNamedSpecifierReport = (problem, checkNamedSpecifiers) =>
|
|
|
26
26
|
@param {import('eslint').Rule.RuleContext} context
|
|
27
27
|
*/
|
|
28
28
|
const create = context => {
|
|
29
|
-
const
|
|
29
|
+
const isCheckNamedSpecifiers = context.options[1]?.checkNamedSpecifiers !== false;
|
|
30
30
|
const fakeContext = Object.create(context, {
|
|
31
31
|
report: {
|
|
32
32
|
value(problem) {
|
|
33
|
-
if (shouldIgnoreNamedSpecifierReport(problem,
|
|
33
|
+
if (shouldIgnoreNamedSpecifierReport(problem, isCheckNamedSpecifiers)) {
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -98,13 +98,51 @@ const create = context => {
|
|
|
98
98
|
|
|
99
99
|
options.comments = options.comments.map(comment => comment.toLowerCase());
|
|
100
100
|
|
|
101
|
-
const
|
|
101
|
+
const configuredGlobals = {
|
|
102
102
|
...(globals[`es${context.languageOptions.ecmaVersion}`] ?? globals.builtins),
|
|
103
103
|
...context.languageOptions.globals,
|
|
104
|
-
...options.overrideGlobals,
|
|
105
104
|
};
|
|
106
105
|
const checked = new WeakSet();
|
|
107
106
|
|
|
107
|
+
const getAllowedGlobalValue = reference => {
|
|
108
|
+
const {identifier, resolved} = reference;
|
|
109
|
+
const {name} = identifier;
|
|
110
|
+
|
|
111
|
+
if (Object.hasOwn(options.overrideGlobals, name)) {
|
|
112
|
+
const overrideGlobalValue = options.overrideGlobals[name];
|
|
113
|
+
|
|
114
|
+
if (overrideGlobalValue === 'off') {
|
|
115
|
+
return 'off';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (resolved && !sourceCode.isGlobalReference(identifier)) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return overrideGlobalValue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (
|
|
126
|
+
Object.hasOwn(configuredGlobals, name)
|
|
127
|
+
&& configuredGlobals[name] === 'off'
|
|
128
|
+
) {
|
|
129
|
+
return 'off';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!sourceCode.isGlobalReference(identifier)) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (
|
|
137
|
+
!Object.hasOwn(configuredGlobals, name)
|
|
138
|
+
&& !resolved?.eslintExplicitGlobal
|
|
139
|
+
) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return resolved?.writeable ? 'writable' : 'readonly';
|
|
144
|
+
};
|
|
145
|
+
|
|
108
146
|
function * getFunctionContextProblems(node, reason, root = node) {
|
|
109
147
|
const messageId = functionContextReferenceMessageIds.get(node.type);
|
|
110
148
|
if (messageId) {
|
|
@@ -173,18 +211,20 @@ const create = context => {
|
|
|
173
211
|
continue;
|
|
174
212
|
}
|
|
175
213
|
|
|
176
|
-
|
|
214
|
+
const allowedGlobalValue = getAllowedGlobalValue(reference);
|
|
215
|
+
let problemReason = reason;
|
|
216
|
+
|
|
217
|
+
if (allowedGlobalValue !== undefined && allowedGlobalValue !== 'off') {
|
|
177
218
|
if (reference.isReadOnly()) {
|
|
178
219
|
continue;
|
|
179
220
|
}
|
|
180
221
|
|
|
181
|
-
const
|
|
182
|
-
const isGlobalWritable = [true, 'writable', 'writeable'].includes(globalsValue);
|
|
222
|
+
const isGlobalWritable = [true, 'writable', 'writeable'].includes(allowedGlobalValue);
|
|
183
223
|
if (isGlobalWritable) {
|
|
184
224
|
continue;
|
|
185
225
|
}
|
|
186
226
|
|
|
187
|
-
|
|
227
|
+
problemReason += ' (global variable is not writable)';
|
|
188
228
|
}
|
|
189
229
|
|
|
190
230
|
// Could consider checking for typeof operator here, like in no-undef?
|
|
@@ -192,7 +232,7 @@ const create = context => {
|
|
|
192
232
|
context.report({
|
|
193
233
|
node: identifier,
|
|
194
234
|
messageId: MESSAGE_ID_EXTERNALLY_SCOPED_VARIABLE,
|
|
195
|
-
data: {name: identifier.name, reason},
|
|
235
|
+
data: {name: identifier.name, reason: problemReason},
|
|
196
236
|
});
|
|
197
237
|
}
|
|
198
238
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import {isRegExp} from 'node:util/types';
|
|
3
|
-
import helperValidatorIdentifier from '@babel/helper-validator-identifier';
|
|
4
3
|
import {
|
|
5
4
|
getAvailableVariableName,
|
|
6
5
|
cartesianProductSamples,
|
|
@@ -9,6 +8,7 @@ import {
|
|
|
9
8
|
upperFirst,
|
|
10
9
|
lowerFirst,
|
|
11
10
|
isVirtualFilename,
|
|
11
|
+
isIdentifierName,
|
|
12
12
|
} from './utils/index.js';
|
|
13
13
|
import {defaultReplacements, defaultAllowList, defaultIgnore} from './shared/name-replacements.js';
|
|
14
14
|
import {
|
|
@@ -24,7 +24,6 @@ import {functionTypes} from './ast/index.js';
|
|
|
24
24
|
const MESSAGE_ID_REPLACE = 'replace';
|
|
25
25
|
const MESSAGE_ID_SUGGESTION = 'suggestion';
|
|
26
26
|
const MESSAGE_ID_RENAME = 'rename';
|
|
27
|
-
const {isIdentifierName} = helperValidatorIdentifier;
|
|
28
27
|
const anotherNameMessage = 'A more descriptive name will do too.';
|
|
29
28
|
const messages = {
|
|
30
29
|
[MESSAGE_ID_REPLACE]: `The {{nameTypeText}} \`{{discouragedName}}\` should be named \`{{replacement}}\`. ${anotherNameMessage}`,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import {getFunctionHeadLocation, getFunctionNameWithKind, isOpeningParenToken} from '@eslint-community/eslint-utils';
|
|
3
|
-
import helperValidatorIdentifier from '@babel/helper-validator-identifier';
|
|
4
3
|
import {camelCase} from 'change-case';
|
|
5
4
|
import {
|
|
6
5
|
getClassHeadLocation,
|
|
@@ -9,11 +8,10 @@ import {
|
|
|
9
8
|
getAvailableVariableName,
|
|
10
9
|
upperFirst,
|
|
11
10
|
isVirtualFilename,
|
|
11
|
+
isIdentifierName,
|
|
12
12
|
} from './utils/index.js';
|
|
13
13
|
import {isMemberExpression} from './ast/index.js';
|
|
14
14
|
|
|
15
|
-
const {isIdentifierName} = helperValidatorIdentifier;
|
|
16
|
-
|
|
17
15
|
const MESSAGE_ID_ERROR = 'no-anonymous-default-export/error';
|
|
18
16
|
const MESSAGE_ID_SUGGESTION = 'no-anonymous-default-export/suggestion';
|
|
19
17
|
const messages = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import isIdentifier from 'is-identifier';
|
|
2
2
|
import {findVariable} from '@eslint-community/eslint-utils';
|
|
3
3
|
import typedArray from './shared/typed-array.js';
|
|
4
4
|
import {isMethodCall, isUndefined} from './ast/index.js';
|
|
@@ -22,11 +22,6 @@ const ERROR_WITH_NAME_MESSAGE_ID = 'error-with-name';
|
|
|
22
22
|
const ERROR_WITHOUT_NAME_MESSAGE_ID = 'error-without-name';
|
|
23
23
|
const REPLACE_WITH_NAME_MESSAGE_ID = 'replace-with-name';
|
|
24
24
|
const REPLACE_WITHOUT_NAME_MESSAGE_ID = 'replace-without-name';
|
|
25
|
-
const {
|
|
26
|
-
isIdentifierName,
|
|
27
|
-
isKeyword,
|
|
28
|
-
isStrictBindReservedWord,
|
|
29
|
-
} = helperValidatorIdentifier;
|
|
30
25
|
const messages = {
|
|
31
26
|
[ERROR_WITH_NAME_MESSAGE_ID]: 'Do not pass function `{{name}}` directly to `.{{method}}(…)`.',
|
|
32
27
|
[ERROR_WITHOUT_NAME_MESSAGE_ID]: 'Do not pass function directly to `.{{method}}(…)`.',
|
|
@@ -301,10 +296,7 @@ function isDefinitelyNotFunctionValue(node, context, visitedVariables = new Set(
|
|
|
301
296
|
: false;
|
|
302
297
|
}
|
|
303
298
|
|
|
304
|
-
const isValidParameterName =
|
|
305
|
-
isIdentifierName(name)
|
|
306
|
-
&& !isKeyword(name)
|
|
307
|
-
&& !isStrictBindReservedWord(name, true);
|
|
299
|
+
const isValidParameterName = isIdentifier;
|
|
308
300
|
|
|
309
301
|
function getSuggestionParameters(callExpression, callback, parameters) {
|
|
310
302
|
if (callback.type !== 'Identifier') {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {hasSideEffect} from '@eslint-community/eslint-utils';
|
|
2
|
+
import {isBooleanLiteral} from './ast/index.js';
|
|
2
3
|
import {getParenthesizedText, isParenthesized, isBoolean} from './utils/index.js';
|
|
3
4
|
|
|
4
5
|
const MESSAGE_ID_ERROR = 'no-chained-comparison/error';
|
|
@@ -13,7 +14,6 @@ const equalityOperators = new Set(['===', '!==', '==', '!=']);
|
|
|
13
14
|
const comparisonOperators = orderingOperators.union(equalityOperators);
|
|
14
15
|
|
|
15
16
|
const isComparison = node => node.type === 'BinaryExpression' && comparisonOperators.has(node.operator);
|
|
16
|
-
const isBooleanLiteral = node => node.type === 'Literal' && typeof node.value === 'boolean';
|
|
17
17
|
|
|
18
18
|
// The `&&` rewrite is a `LogicalExpression`, which binds looser than any comparison. It can only
|
|
19
19
|
// replace `node` where a surrounding operator won't recapture it: at the top of an expression or
|
|
@@ -134,10 +134,7 @@ const create = context => {
|
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
context.on('BinaryExpression', node => {
|
|
137
|
-
if (
|
|
138
|
-
node.operator === 'in'
|
|
139
|
-
&& !isStaticPropertyKey(node.left)
|
|
140
|
-
)) {
|
|
137
|
+
if (node.operator !== 'in' || isStaticPropertyKey(node.left)) {
|
|
141
138
|
return;
|
|
142
139
|
}
|
|
143
140
|
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import {isCommentToken, hasSideEffect} from '@eslint-community/eslint-utils';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
containsSuspensionPoint,
|
|
4
|
+
getReferences,
|
|
5
|
+
isReactHookName,
|
|
6
|
+
trackBranchExits,
|
|
7
|
+
} from './utils/index.js';
|
|
3
8
|
|
|
4
9
|
const MESSAGE_ID = 'no-declarations-before-early-exit';
|
|
5
10
|
const messages = {
|
|
@@ -23,10 +28,6 @@ function isGuardStatement(node, branchAlwaysExits) {
|
|
|
23
28
|
return consequentExits !== alternateExits;
|
|
24
29
|
}
|
|
25
30
|
|
|
26
|
-
// https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts
|
|
27
|
-
const isReactHookName = name =>
|
|
28
|
-
name === 'use' || /^use[\dA-Z]/.test(name);
|
|
29
|
-
|
|
30
31
|
// Matches `useFoo(…)` and `Namespace.useFoo(…)` (for example, `React.useMemo(…)`).
|
|
31
32
|
const isReactHookCall = node => {
|
|
32
33
|
if (node?.type !== 'CallExpression') {
|
|
@@ -142,7 +142,7 @@ const getLocalUpdateFrame = knownErrorVariableFrames =>
|
|
|
142
142
|
?? knownErrorVariableFrames.at(-1);
|
|
143
143
|
|
|
144
144
|
const updateExistingKnownErrorVariable = (knownErrorVariableFrames, variable, constructorName) => {
|
|
145
|
-
let
|
|
145
|
+
let isCrossesFunctionBoundary = false;
|
|
146
146
|
|
|
147
147
|
for (const frame of knownErrorVariableFrames.toReversed()) {
|
|
148
148
|
if (frame.knownErrorVariables.has(variable)) {
|
|
@@ -151,7 +151,7 @@ const updateExistingKnownErrorVariable = (knownErrorVariableFrames, variable, co
|
|
|
151
151
|
return true;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
if (!
|
|
154
|
+
if (!isCrossesFunctionBoundary) {
|
|
155
155
|
frame.knownErrorVariables.set(variable, undefined);
|
|
156
156
|
}
|
|
157
157
|
|
|
@@ -160,7 +160,7 @@ const updateExistingKnownErrorVariable = (knownErrorVariableFrames, variable, co
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
if (frame.isFunctionBoundary) {
|
|
163
|
-
|
|
163
|
+
isCrossesFunctionBoundary = true;
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
|
package/rules/no-for-loop.js
CHANGED
|
@@ -876,14 +876,14 @@ const create = context => {
|
|
|
876
876
|
|
|
877
877
|
let declarationElement = element;
|
|
878
878
|
let declarationType = 'const';
|
|
879
|
-
let
|
|
879
|
+
let isRemoveDeclaration = true;
|
|
880
880
|
|
|
881
881
|
if (elementNode) {
|
|
882
882
|
if (elementNode.id.type === 'ObjectPattern' || elementNode.id.type === 'ArrayPattern') {
|
|
883
|
-
|
|
883
|
+
isRemoveDeclaration = arrayReferences.length === 1;
|
|
884
884
|
}
|
|
885
885
|
|
|
886
|
-
if (
|
|
886
|
+
if (isRemoveDeclaration) {
|
|
887
887
|
declarationType = elementNode.parent.kind;
|
|
888
888
|
declarationElement = sourceCode.getText(elementNode.id);
|
|
889
889
|
}
|
|
@@ -911,7 +911,7 @@ const create = context => {
|
|
|
911
911
|
}
|
|
912
912
|
|
|
913
913
|
if (elementNode) {
|
|
914
|
-
yield
|
|
914
|
+
yield isRemoveDeclaration
|
|
915
915
|
? fixer.removeRange(getRemovalRange(elementNode, sourceCode))
|
|
916
916
|
: fixer.replaceText(elementNode.init, element);
|
|
917
917
|
}
|
|
@@ -90,12 +90,12 @@ function reportObjectPatternAndShouldSkipPropertyCheck(report, node, options) {
|
|
|
90
90
|
return true;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
const
|
|
93
|
+
const isAssignmentKeyEqualsValue = parent.key.name === parent.value.name;
|
|
94
94
|
|
|
95
95
|
const valueIsInvalid = parent.value.name && Boolean(keyword);
|
|
96
96
|
|
|
97
97
|
// Ignore destructuring if the option is set, unless a new identifier is created
|
|
98
|
-
if (valueIsInvalid && !
|
|
98
|
+
if (valueIsInvalid && !isAssignmentKeyEqualsValue) {
|
|
99
99
|
report(node, keyword);
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {findVariable, getStaticValue} from '@eslint-community/eslint-utils';
|
|
2
2
|
import {isMethodCall} from './ast/index.js';
|
|
3
3
|
import {isReference, isSame, unwrapExpression} from './utils/comparison.js';
|
|
4
|
-
import {isKnownNonMap} from './utils/index.js';
|
|
4
|
+
import {isComparableStaticValue, isKnownNonMap} from './utils/index.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
@import {TSESTree as ESTree} from '@typescript-eslint/types';
|
|
@@ -21,13 +21,6 @@ const skippedNodeTypes = new Set([
|
|
|
21
21
|
'FunctionExpression',
|
|
22
22
|
]);
|
|
23
23
|
|
|
24
|
-
const isComparableStaticValue = value =>
|
|
25
|
-
value === null
|
|
26
|
-
|| (
|
|
27
|
-
typeof value !== 'object'
|
|
28
|
-
&& typeof value !== 'function'
|
|
29
|
-
);
|
|
30
|
-
|
|
31
24
|
const isSameValueZero = (left, right) =>
|
|
32
25
|
left === right
|
|
33
26
|
|| (
|
|
@@ -132,7 +125,7 @@ function patternContainsSameBinding(node, mapHasRoot, context) {
|
|
|
132
125
|
|
|
133
126
|
function hasCallExpression(node, context) {
|
|
134
127
|
const {visitorKeys} = context.sourceCode;
|
|
135
|
-
let
|
|
128
|
+
let isResult = false;
|
|
136
129
|
|
|
137
130
|
function visit(node) {
|
|
138
131
|
if (!node) {
|
|
@@ -142,14 +135,14 @@ function hasCallExpression(node, context) {
|
|
|
142
135
|
node = unwrapExpression(node);
|
|
143
136
|
|
|
144
137
|
if (
|
|
145
|
-
|
|
138
|
+
isResult
|
|
146
139
|
|| skippedNodeTypes.has(node.type)
|
|
147
140
|
) {
|
|
148
141
|
return;
|
|
149
142
|
}
|
|
150
143
|
|
|
151
144
|
if (node.type === 'CallExpression') {
|
|
152
|
-
|
|
145
|
+
isResult = true;
|
|
153
146
|
return;
|
|
154
147
|
}
|
|
155
148
|
|
|
@@ -170,7 +163,7 @@ function hasCallExpression(node, context) {
|
|
|
170
163
|
|
|
171
164
|
visit(node);
|
|
172
165
|
|
|
173
|
-
return
|
|
166
|
+
return isResult;
|
|
174
167
|
}
|
|
175
168
|
|
|
176
169
|
function areDifferentKeys(left, right, context) {
|
|
@@ -353,11 +346,11 @@ function isNestedMapHasGuard(node, mapHasCall, context) {
|
|
|
353
346
|
|
|
354
347
|
function hasSameMapHasCall(node, mapHasCall, context) {
|
|
355
348
|
const {visitorKeys} = context.sourceCode;
|
|
356
|
-
let
|
|
349
|
+
let isResult = false;
|
|
357
350
|
|
|
358
351
|
function visit(node) {
|
|
359
352
|
if (
|
|
360
|
-
|
|
353
|
+
isResult
|
|
361
354
|
|| !node
|
|
362
355
|
|| skippedNodeTypes.has(node.type)
|
|
363
356
|
) {
|
|
@@ -370,7 +363,7 @@ function hasSameMapHasCall(node, mapHasCall, context) {
|
|
|
370
363
|
nestedMapHasCall
|
|
371
364
|
&& isSameMapReceiver(unwrapExpression(mapHasCall).callee.object, unwrapExpression(nestedMapHasCall).callee.object, context)
|
|
372
365
|
) {
|
|
373
|
-
|
|
366
|
+
isResult = true;
|
|
374
367
|
return;
|
|
375
368
|
}
|
|
376
369
|
|
|
@@ -391,16 +384,16 @@ function hasSameMapHasCall(node, mapHasCall, context) {
|
|
|
391
384
|
|
|
392
385
|
visit(node);
|
|
393
386
|
|
|
394
|
-
return
|
|
387
|
+
return isResult;
|
|
395
388
|
}
|
|
396
389
|
|
|
397
390
|
function hasMapReceiverWrite(node, mapHasCall, context) {
|
|
398
391
|
const {visitorKeys} = context.sourceCode;
|
|
399
|
-
let
|
|
392
|
+
let isResult = false;
|
|
400
393
|
|
|
401
394
|
function visit(node) {
|
|
402
395
|
if (
|
|
403
|
-
|
|
396
|
+
isResult
|
|
404
397
|
|| !node
|
|
405
398
|
|| skippedNodeTypes.has(node.type)
|
|
406
399
|
|| isNestedMapHasGuard(node, mapHasCall, context)
|
|
@@ -409,7 +402,7 @@ function hasMapReceiverWrite(node, mapHasCall, context) {
|
|
|
409
402
|
}
|
|
410
403
|
|
|
411
404
|
if (isMapReceiverWrite(node, mapHasCall, context)) {
|
|
412
|
-
|
|
405
|
+
isResult = true;
|
|
413
406
|
return;
|
|
414
407
|
}
|
|
415
408
|
|
|
@@ -430,7 +423,7 @@ function hasMapReceiverWrite(node, mapHasCall, context) {
|
|
|
430
423
|
|
|
431
424
|
visit(node);
|
|
432
425
|
|
|
433
|
-
return
|
|
426
|
+
return isResult;
|
|
434
427
|
}
|
|
435
428
|
|
|
436
429
|
function getMapAccessProblems(node, mapHasCall, context, reportedAccessKeys) {
|
|
@@ -97,12 +97,9 @@ const create = context => {
|
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
context.on('ExportSpecifier', specifier => {
|
|
100
|
-
if (!(
|
|
101
|
-
isValueExport(specifier)
|
|
100
|
+
if (!(isValueExport(specifier)
|
|
102
101
|
&& specifier.exported.name === 'default'
|
|
103
|
-
&& isValueExport(specifier.parent)
|
|
104
|
-
&& !specifier.parent.source
|
|
105
|
-
)) {
|
|
102
|
+
&& isValueExport(specifier.parent)) || specifier.parent.source) {
|
|
106
103
|
return;
|
|
107
104
|
}
|
|
108
105
|
|