eslint 10.2.1 → 10.3.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/README.md +1 -1
- package/lib/config/config-loader.js +1 -1
- package/lib/linter/code-path-analysis/code-path-analyzer.js +7 -7
- package/lib/linter/code-path-analysis/id-generator.js +1 -2
- package/lib/rules/no-unused-private-class-members.js +201 -1
- package/lib/rules/no-useless-concat.js +2 -2
- package/lib/rules/no-useless-constructor.js +3 -2
- package/lib/rules/require-await.js +6 -4
- package/lib/rules/utils/ast-utils.js +36 -4
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -360,7 +360,7 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
|
|
|
360
360
|
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a></p><h3>Gold Sponsors</h3>
|
|
361
361
|
<p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a></p><h3>Silver Sponsors</h3>
|
|
362
362
|
<p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/d472863/logo.png" alt="Vite" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/2d6c3b6/logo.png" alt="Liftoff" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
|
|
363
|
-
<p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://opensource.sap.com"><img src="https://avatars.githubusercontent.com/u/2531208" alt="SAP" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="https://depot.dev"><img src="https://images.opencollective.com/depot/39125a1/logo.png" alt="Depot" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="TestMu AI Open Source Office (Formerly LambdaTest)" height="32"></a></p>
|
|
363
|
+
<p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://opensource.sap.com"><img src="https://avatars.githubusercontent.com/u/2531208" alt="SAP" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="https://depot.dev"><img src="https://images.opencollective.com/depot/39125a1/logo.png" alt="Depot" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://citadel.co.jp"><img src="https://avatars.githubusercontent.com/u/75781367" alt="Citadel AI" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="TestMu AI Open Source Office (Formerly LambdaTest)" height="32"></a></p>
|
|
364
364
|
<h3>Technology Sponsors</h3>
|
|
365
365
|
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
|
|
366
366
|
<p><a href="https://netlify.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg" alt="Netlify" height="32"></a> <a href="https://algolia.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg" alt="Algolia" height="32"></a> <a href="https://1password.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg" alt="1Password" height="32"></a></p>
|
|
@@ -230,7 +230,7 @@ async function loadConfigFile(filePath, hasUnstableNativeNodeJsTSConfigFlag) {
|
|
|
230
230
|
* the require cache only if the file has been changed.
|
|
231
231
|
*/
|
|
232
232
|
if (importedConfigFileModificationTime.get(filePath) !== mtime) {
|
|
233
|
-
delete require.cache[filePath];
|
|
233
|
+
delete require.cache?.[filePath];
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
const isTS = isFileTS(filePath);
|
|
@@ -77,8 +77,8 @@ function getLabel(node) {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
|
-
* Checks whether
|
|
81
|
-
*
|
|
80
|
+
* Checks whether a given logical expression node takes different paths for the
|
|
81
|
+
* `true` and `false` cases.
|
|
82
82
|
* @param {ASTNode} node A node to check.
|
|
83
83
|
* @returns {boolean} `true` if the node is a test of a choice statement.
|
|
84
84
|
*/
|
|
@@ -107,8 +107,8 @@ function isForkingByTrueOrFalse(node) {
|
|
|
107
107
|
/**
|
|
108
108
|
* Gets the boolean value of a given literal node.
|
|
109
109
|
*
|
|
110
|
-
* This is used to detect
|
|
111
|
-
* Statements preceded by an
|
|
110
|
+
* This is used to detect infinite loops (e.g. `while (true) {}`).
|
|
111
|
+
* Statements preceded by an infinite loop are unreachable if the loop didn't
|
|
112
112
|
* have any `break` statement.
|
|
113
113
|
* @param {ASTNode} node A node to get.
|
|
114
114
|
* @returns {boolean|undefined} a boolean value if the node is a Literal node,
|
|
@@ -221,8 +221,8 @@ function forwardCurrentToHead(analyzer, node) {
|
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
/**
|
|
224
|
-
* Updates the current segment with empty.
|
|
225
|
-
* This is called
|
|
224
|
+
* Updates the current segment with an empty array.
|
|
225
|
+
* This is called when a code path ends.
|
|
226
226
|
* @param {CodePathAnalyzer} analyzer The instance.
|
|
227
227
|
* @param {ASTNode} node The current AST node.
|
|
228
228
|
* @returns {void}
|
|
@@ -727,7 +727,7 @@ function postprocess(analyzer, node) {
|
|
|
727
727
|
* a = () => {}
|
|
728
728
|
* }
|
|
729
729
|
*
|
|
730
|
-
* In this case,
|
|
730
|
+
* In this case, the ArrowFunctionExpression code path is closed first,
|
|
731
731
|
* and then we need to close the code path for the PropertyDefinition
|
|
732
732
|
* value.
|
|
733
733
|
*/
|
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
"use strict";
|
|
7
7
|
|
|
8
|
+
//------------------------------------------------------------------------------
|
|
9
|
+
// Requirements
|
|
10
|
+
//------------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
const astUtils = require("./utils/ast-utils");
|
|
13
|
+
|
|
8
14
|
//------------------------------------------------------------------------------
|
|
9
15
|
// Rule Definition
|
|
10
16
|
//------------------------------------------------------------------------------
|
|
@@ -13,6 +19,7 @@
|
|
|
13
19
|
module.exports = {
|
|
14
20
|
meta: {
|
|
15
21
|
type: "problem",
|
|
22
|
+
hasSuggestions: true,
|
|
16
23
|
|
|
17
24
|
docs: {
|
|
18
25
|
description: "Disallow unused private class members",
|
|
@@ -25,12 +32,168 @@ module.exports = {
|
|
|
25
32
|
messages: {
|
|
26
33
|
unusedPrivateClassMember:
|
|
27
34
|
"'{{classMemberName}}' is defined but never used.",
|
|
35
|
+
removeUnusedPrivateClassMember:
|
|
36
|
+
"Remove unused private class member '{{classMemberName}}'.",
|
|
28
37
|
},
|
|
29
38
|
},
|
|
30
39
|
|
|
31
40
|
create(context) {
|
|
41
|
+
const sourceCode = context.sourceCode;
|
|
32
42
|
const trackedClasses = [];
|
|
33
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Gets the start index of the line that contains a given token or node.
|
|
46
|
+
* @param {ASTNode|Token|Comment} nodeOrToken The token or node to check
|
|
47
|
+
* @returns {number} The line start index
|
|
48
|
+
*/
|
|
49
|
+
function getLineStartIndex(nodeOrToken) {
|
|
50
|
+
return nodeOrToken.range[0] - nodeOrToken.loc.start.column;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Checks whether a token or node starts on its own line, preceded only by whitespace.
|
|
55
|
+
* @param {ASTNode|Token|Comment} nodeOrToken The token or node to check
|
|
56
|
+
* @returns {boolean} Whether the token or node starts on its own line
|
|
57
|
+
*/
|
|
58
|
+
function startsOnOwnLine(nodeOrToken) {
|
|
59
|
+
return (
|
|
60
|
+
sourceCode.getTokenBefore(nodeOrToken, {
|
|
61
|
+
includeComments: true,
|
|
62
|
+
}).loc.end.line !== nodeOrToken.loc.start.line
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Gets leading comments that are directly attached to a class member.
|
|
68
|
+
* @param {ASTNode} classMemberNode The class member node
|
|
69
|
+
* @returns {Comment[]} Leading comments to remove with the member
|
|
70
|
+
*/
|
|
71
|
+
function getLeadingComments(classMemberNode) {
|
|
72
|
+
const commentsBefore =
|
|
73
|
+
sourceCode.getCommentsBefore(classMemberNode);
|
|
74
|
+
const lastNonLeadingCommentIndex = commentsBefore.findLastIndex(
|
|
75
|
+
(comment, index, self) => {
|
|
76
|
+
const next =
|
|
77
|
+
index < self.length - 1
|
|
78
|
+
? self[index + 1]
|
|
79
|
+
: classMemberNode;
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
!startsOnOwnLine(comment) ||
|
|
83
|
+
next.loc.start.line - comment.loc.end.line > 1
|
|
84
|
+
);
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return commentsBefore.slice(lastNonLeadingCommentIndex + 1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Checks whether a class member shares its line with another token.
|
|
93
|
+
* @param {ASTNode} classMemberNode The class member node
|
|
94
|
+
* @returns {boolean} Whether the member shares its line with another token
|
|
95
|
+
*/
|
|
96
|
+
function sharesLineWithAnotherToken(classMemberNode) {
|
|
97
|
+
const previousToken = sourceCode.getTokenBefore(classMemberNode);
|
|
98
|
+
const nextToken = sourceCode.getTokenAfter(classMemberNode);
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
previousToken.loc.end.line === classMemberNode.loc.start.line ||
|
|
102
|
+
nextToken.loc.start.line === classMemberNode.loc.end.line
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Gets trailing comments that are directly attached to a class member.
|
|
108
|
+
* Same-line trailing comments are preserved when another token shares
|
|
109
|
+
* the line, because the comment might describe the remaining code rather
|
|
110
|
+
* than the unused member alone.
|
|
111
|
+
* @param {ASTNode} classMemberNode The class member node
|
|
112
|
+
* @returns {Comment[]} Trailing comments to remove with the member
|
|
113
|
+
*/
|
|
114
|
+
function getTrailingComments(classMemberNode) {
|
|
115
|
+
if (sharesLineWithAnotherToken(classMemberNode)) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return sourceCode
|
|
120
|
+
.getCommentsAfter(classMemberNode)
|
|
121
|
+
.filter(
|
|
122
|
+
comment =>
|
|
123
|
+
comment.loc.start.line === classMemberNode.loc.end.line,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Gets the token after which a semicolon should be inserted when removing a class member.
|
|
129
|
+
* @param {ASTNode} classMemberNode The member that would be removed
|
|
130
|
+
* @returns {Token|null} The token after which a semicolon should be inserted, or null if no semicolon is needed
|
|
131
|
+
*/
|
|
132
|
+
function getSemicolonInsertionToken(classMemberNode) {
|
|
133
|
+
const nextToken = sourceCode.getTokenAfter(classMemberNode);
|
|
134
|
+
|
|
135
|
+
if (
|
|
136
|
+
astUtils.canContinueExpressionInClassBody(nextToken) &&
|
|
137
|
+
astUtils.needsPrecedingSemicolon(sourceCode, classMemberNode)
|
|
138
|
+
) {
|
|
139
|
+
return sourceCode.getTokenBefore(classMemberNode);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Gets the replacement range for removing an unused class member.
|
|
147
|
+
* @param {ASTNode} classMemberNode The member that would be removed
|
|
148
|
+
* @returns {number[]} The text range to remove
|
|
149
|
+
*/
|
|
150
|
+
function getMemberRemovalRange(classMemberNode) {
|
|
151
|
+
const leadingComments = getLeadingComments(classMemberNode);
|
|
152
|
+
const trailingComments = getTrailingComments(classMemberNode);
|
|
153
|
+
const shouldRemoveLeadingComments =
|
|
154
|
+
leadingComments.length > 0 &&
|
|
155
|
+
!sharesLineWithAnotherToken(classMemberNode);
|
|
156
|
+
const lastItemToRemove =
|
|
157
|
+
trailingComments.length > 0
|
|
158
|
+
? trailingComments.at(-1)
|
|
159
|
+
: classMemberNode;
|
|
160
|
+
|
|
161
|
+
const previousToken = sourceCode.getTokenBefore(classMemberNode);
|
|
162
|
+
const nextToken = sourceCode.getTokenAfter(lastItemToRemove, {
|
|
163
|
+
includeComments: true,
|
|
164
|
+
});
|
|
165
|
+
const nextTokenStartsOnNewLine =
|
|
166
|
+
nextToken.loc.start.line > lastItemToRemove.loc.end.line;
|
|
167
|
+
const shouldRemoveOwnLine =
|
|
168
|
+
!shouldRemoveLeadingComments &&
|
|
169
|
+
startsOnOwnLine(classMemberNode) &&
|
|
170
|
+
nextTokenStartsOnNewLine;
|
|
171
|
+
let start = classMemberNode.range[0];
|
|
172
|
+
let end = lastItemToRemove.range[1];
|
|
173
|
+
|
|
174
|
+
if (shouldRemoveLeadingComments) {
|
|
175
|
+
start = nextTokenStartsOnNewLine
|
|
176
|
+
? getLineStartIndex(leadingComments[0])
|
|
177
|
+
: leadingComments[0].range[0];
|
|
178
|
+
end = nextTokenStartsOnNewLine
|
|
179
|
+
? getLineStartIndex(nextToken)
|
|
180
|
+
: nextToken.range[0];
|
|
181
|
+
} else if (shouldRemoveOwnLine) {
|
|
182
|
+
start = getLineStartIndex(classMemberNode);
|
|
183
|
+
end = getLineStartIndex(nextToken);
|
|
184
|
+
} else if (
|
|
185
|
+
previousToken.loc.end.line === classMemberNode.loc.start.line
|
|
186
|
+
) {
|
|
187
|
+
start = previousToken.range[1];
|
|
188
|
+
} else if (
|
|
189
|
+
nextToken.loc.start.line === lastItemToRemove.loc.end.line
|
|
190
|
+
) {
|
|
191
|
+
end = nextToken.range[0];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return [start, end];
|
|
195
|
+
}
|
|
196
|
+
|
|
34
197
|
/**
|
|
35
198
|
* Check whether the current node is in a write only assignment.
|
|
36
199
|
* @param {ASTNode} privateIdentifierNode Node referring to a private identifier
|
|
@@ -86,6 +249,7 @@ module.exports = {
|
|
|
86
249
|
if (bodyMember.key.type === "PrivateIdentifier") {
|
|
87
250
|
privateMembers.set(bodyMember.key.name, {
|
|
88
251
|
declaredNode: bodyMember,
|
|
252
|
+
hasReference: false,
|
|
89
253
|
isAccessor:
|
|
90
254
|
bodyMember.type === "MethodDefinition" &&
|
|
91
255
|
(bodyMember.kind === "set" ||
|
|
@@ -128,6 +292,8 @@ module.exports = {
|
|
|
128
292
|
return;
|
|
129
293
|
}
|
|
130
294
|
|
|
295
|
+
memberDefinition.hasReference = true;
|
|
296
|
+
|
|
131
297
|
/*
|
|
132
298
|
* Any usage of an accessor is considered a read, as the getter/setter can have
|
|
133
299
|
* side-effects in its definition.
|
|
@@ -199,11 +365,12 @@ module.exports = {
|
|
|
199
365
|
|
|
200
366
|
for (const [
|
|
201
367
|
classMemberName,
|
|
202
|
-
{ declaredNode, isUsed },
|
|
368
|
+
{ declaredNode, hasReference, isUsed },
|
|
203
369
|
] of unusedPrivateMembers.entries()) {
|
|
204
370
|
if (isUsed) {
|
|
205
371
|
continue;
|
|
206
372
|
}
|
|
373
|
+
|
|
207
374
|
context.report({
|
|
208
375
|
node: declaredNode,
|
|
209
376
|
loc: declaredNode.key.loc,
|
|
@@ -211,6 +378,39 @@ module.exports = {
|
|
|
211
378
|
data: {
|
|
212
379
|
classMemberName: `#${classMemberName}`,
|
|
213
380
|
},
|
|
381
|
+
suggest: [
|
|
382
|
+
{
|
|
383
|
+
messageId: "removeUnusedPrivateClassMember",
|
|
384
|
+
data: {
|
|
385
|
+
classMemberName: `#${classMemberName}`,
|
|
386
|
+
},
|
|
387
|
+
*fix(fixer) {
|
|
388
|
+
if (hasReference) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const removalRange =
|
|
393
|
+
getMemberRemovalRange(declaredNode);
|
|
394
|
+
const semicolonInsertionToken =
|
|
395
|
+
getSemicolonInsertionToken(
|
|
396
|
+
declaredNode,
|
|
397
|
+
);
|
|
398
|
+
const removalFix = fixer.replaceTextRange(
|
|
399
|
+
removalRange,
|
|
400
|
+
"",
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
yield removalFix;
|
|
404
|
+
|
|
405
|
+
if (semicolonInsertionToken) {
|
|
406
|
+
yield fixer.insertTextAfter(
|
|
407
|
+
semicolonInsertionToken,
|
|
408
|
+
";",
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
],
|
|
214
414
|
});
|
|
215
415
|
}
|
|
216
416
|
},
|
|
@@ -33,7 +33,7 @@ function isConcatOperatorToken(token) {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* Gets the right most node on the left side of a BinaryExpression with + operator.
|
|
37
37
|
* @param {ASTNode} node A BinaryExpression node to check.
|
|
38
38
|
* @returns {ASTNode} node
|
|
39
39
|
*/
|
|
@@ -47,7 +47,7 @@ function getLeft(node) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
*
|
|
50
|
+
* Gets the left most node on the right side of a BinaryExpression with + operator.
|
|
51
51
|
* @param {ASTNode} node A BinaryExpression node to check.
|
|
52
52
|
* @returns {ASTNode} node
|
|
53
53
|
*/
|
|
@@ -237,8 +237,9 @@ module.exports = {
|
|
|
237
237
|
const nextToken =
|
|
238
238
|
sourceCode.getTokenAfter(node);
|
|
239
239
|
const addSemiColon =
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
astUtils.canContinueExpressionInClassBody(
|
|
241
|
+
nextToken,
|
|
242
|
+
) &&
|
|
242
243
|
astUtils.needsPrecedingSemicolon(
|
|
243
244
|
sourceCode,
|
|
244
245
|
node,
|
|
@@ -110,12 +110,14 @@ module.exports = {
|
|
|
110
110
|
*/
|
|
111
111
|
const nextToken = sourceCode.getTokenAfter(asyncToken);
|
|
112
112
|
const addSemiColon =
|
|
113
|
-
nextToken
|
|
114
|
-
(nextToken.value === "[" || nextToken.value === "(") &&
|
|
115
|
-
(nodeWithAsyncKeyword.type === "MethodDefinition" ||
|
|
113
|
+
((astUtils.isOpeningParenToken(nextToken) &&
|
|
116
114
|
astUtils.isStartOfExpressionStatement(
|
|
117
115
|
nodeWithAsyncKeyword,
|
|
118
|
-
))
|
|
116
|
+
)) ||
|
|
117
|
+
(nodeWithAsyncKeyword.type === "MethodDefinition" &&
|
|
118
|
+
astUtils.canContinueExpressionInClassBody(
|
|
119
|
+
nextToken,
|
|
120
|
+
))) &&
|
|
119
121
|
astUtils.needsPrecedingSemicolon(
|
|
120
122
|
sourceCode,
|
|
121
123
|
nodeWithAsyncKeyword,
|
|
@@ -1258,12 +1258,31 @@ function isStartOfExpressionStatement(node) {
|
|
|
1258
1258
|
}
|
|
1259
1259
|
|
|
1260
1260
|
/**
|
|
1261
|
-
*
|
|
1262
|
-
*
|
|
1263
|
-
*
|
|
1261
|
+
* Checks whether a token can cause continuation of a preceding expression
|
|
1262
|
+
* (for example, of a class field initializer expression) in a class body.
|
|
1263
|
+
* This function checks specifically for tokens that can appear at the start
|
|
1264
|
+
* of a class member: `[` (computed key), `*` (generator method), `in` or `instanceof` (valid keys)
|
|
1265
|
+
* Without a preceding semicolon, these tokens would be parsed as index access or operators.
|
|
1266
|
+
* @param {Token} token The token to check.
|
|
1267
|
+
* @returns {boolean} Whether the token can cause continuation of a preceding expression.
|
|
1268
|
+
*/
|
|
1269
|
+
function canContinueExpressionInClassBody(token) {
|
|
1270
|
+
return (
|
|
1271
|
+
(token.type === "Punctuator" &&
|
|
1272
|
+
(token.value === "[" || token.value === "*")) ||
|
|
1273
|
+
// Different parsers may return these tokens as either "Identifier" or "Keyword"
|
|
1274
|
+
((token.type === "Identifier" || token.type === "Keyword") &&
|
|
1275
|
+
(token.value === "in" || token.value === "instanceof"))
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
/**
|
|
1280
|
+
* Determines whether an opening parenthesis `(`, bracket `[`, asterisk `*`, or backtick ``` ` ``` needs to be preceded by a semicolon.
|
|
1281
|
+
* This opening parenthesis or bracket should be at the start of an `ExpressionStatement`, a `MethodDefinition`, a `PropertyDefinition`,
|
|
1282
|
+
* or at the start of the body of an `ArrowFunctionExpression`.
|
|
1264
1283
|
* @type {(sourceCode: SourceCode, node: ASTNode) => boolean}
|
|
1265
1284
|
* @param {SourceCode} sourceCode The source code object.
|
|
1266
|
-
* @param {ASTNode} node A node at the position where an opening parenthesis or
|
|
1285
|
+
* @param {ASTNode} node A node at the position where an opening parenthesis, bracket, or asterisk will be inserted.
|
|
1267
1286
|
* @returns {boolean} Whether a semicolon is required before the opening parenthesis or bracket.
|
|
1268
1287
|
*/
|
|
1269
1288
|
let needsPrecedingSemicolon;
|
|
@@ -1354,6 +1373,18 @@ let needsPrecedingSemicolon;
|
|
|
1354
1373
|
|
|
1355
1374
|
const prevNode = sourceCode.getNodeByRangeIndex(prevToken.range[0]);
|
|
1356
1375
|
|
|
1376
|
+
// Uninitialized class fields don't need a semicolon
|
|
1377
|
+
if (
|
|
1378
|
+
// Key
|
|
1379
|
+
(prevNode.parent.type === "PropertyDefinition" &&
|
|
1380
|
+
prevNode.parent.key === prevNode) ||
|
|
1381
|
+
// Closing bracket of a computed key
|
|
1382
|
+
(prevNode.type === "PropertyDefinition" &&
|
|
1383
|
+
isClosingBracketToken(prevToken))
|
|
1384
|
+
) {
|
|
1385
|
+
return false;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1357
1388
|
if (
|
|
1358
1389
|
prevNode.type === "TSDeclareFunction" ||
|
|
1359
1390
|
prevNode.parent.type === "TSImportEqualsDeclaration" ||
|
|
@@ -2842,6 +2873,7 @@ module.exports = {
|
|
|
2842
2873
|
isTopLevelExpressionStatement,
|
|
2843
2874
|
isDirective,
|
|
2844
2875
|
isStartOfExpressionStatement,
|
|
2876
|
+
canContinueExpressionInClassBody,
|
|
2845
2877
|
needsPrecedingSemicolon,
|
|
2846
2878
|
isImportAttributeKey,
|
|
2847
2879
|
getOpeningParenOfParams,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.3.0",
|
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -72,6 +72,8 @@
|
|
|
72
72
|
"test": "node Makefile.js test",
|
|
73
73
|
"test:browser": "node Makefile.js cypress",
|
|
74
74
|
"test:cli": "mocha",
|
|
75
|
+
"test:ecosystem": "node tools/test-ecosystem/index.mjs",
|
|
76
|
+
"test:ecosystem:update": "node tools/test-ecosystem/update.mjs",
|
|
75
77
|
"test:emfile": "node tools/check-emfile-handling.js",
|
|
76
78
|
"test:fuzz": "node Makefile.js fuzz",
|
|
77
79
|
"test:performance": "node Makefile.js perf",
|
|
@@ -157,7 +159,7 @@
|
|
|
157
159
|
"@eslint/json": "^1.2.0",
|
|
158
160
|
"@types/esquery": "^1.5.4",
|
|
159
161
|
"@types/node": "^22.13.14",
|
|
160
|
-
"@typescript-eslint/parser": "^8.
|
|
162
|
+
"@typescript-eslint/parser": "^8.58.2",
|
|
161
163
|
"babel-loader": "^8.0.5",
|
|
162
164
|
"c8": "^11.0.0",
|
|
163
165
|
"chai": "^4.0.1",
|
|
@@ -196,7 +198,7 @@
|
|
|
196
198
|
"mocha": "^11.7.1",
|
|
197
199
|
"node-polyfill-webpack-plugin": "^1.0.3",
|
|
198
200
|
"npm-license": "^0.3.3",
|
|
199
|
-
"prettier": "3.8.
|
|
201
|
+
"prettier": "3.8.3",
|
|
200
202
|
"progress": "^2.0.3",
|
|
201
203
|
"proxyquire": "^2.0.1",
|
|
202
204
|
"recast": "^0.23.0",
|
|
@@ -204,7 +206,7 @@
|
|
|
204
206
|
"semver": "^7.5.3",
|
|
205
207
|
"shelljs": "^0.10.0",
|
|
206
208
|
"sinon": "^11.0.0",
|
|
207
|
-
"typescript": "^
|
|
209
|
+
"typescript": "^6.0.3",
|
|
208
210
|
"webpack": "^5.23.0",
|
|
209
211
|
"webpack-cli": "^4.5.0",
|
|
210
212
|
"yorkie": "^2.0.0"
|