eslint 9.38.0 → 9.39.1
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 +2 -2
- package/lib/eslint/eslint.js +17 -1
- package/lib/eslint/worker.js +12 -1
- package/lib/languages/js/source-code/source-code.js +2 -2
- package/lib/linter/file-report.js +1 -1
- package/lib/linter/source-code-traverser.js +11 -5
- package/lib/linter/timing.js +38 -1
- package/lib/rules/complexity.js +3 -4
- package/lib/rules/for-direction.js +4 -1
- package/lib/rules/no-dupe-args.js +12 -1
- package/lib/rules/no-dupe-class-members.js +1 -1
- package/lib/rules/object-shorthand.js +105 -80
- package/lib/rules/utils/ast-utils.js +1 -0
- package/lib/types/index.d.ts +88 -596
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -342,8 +342,8 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
|
|
|
342
342
|
<h3>Platinum Sponsors</h3>
|
|
343
343
|
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="128"></a></p><h3>Gold Sponsors</h3>
|
|
344
344
|
<p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a> <a href="https://trunk.io/"><img src="https://images.opencollective.com/trunkio/fb92d60/avatar.png" alt="trunk.io" height="96"></a> <a href="https://shopify.engineering/"><img src="https://avatars.githubusercontent.com/u/8085" alt="Shopify" height="96"></a></p><h3>Silver Sponsors</h3>
|
|
345
|
-
<p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/e6d15e1/logo.png" alt="Vite" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/
|
|
346
|
-
<p><a href="https://
|
|
345
|
+
<p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/e6d15e1/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://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301" alt="American Express" 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>
|
|
346
|
+
<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://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://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104" alt="Nx" height="32"></a> <a href="https://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465" alt="Mercedes-Benz Group" 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="LambdaTest" height="32"></a></p>
|
|
347
347
|
<h3>Technology Sponsors</h3>
|
|
348
348
|
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.
|
|
349
349
|
<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>
|
package/lib/eslint/eslint.js
CHANGED
|
@@ -17,6 +17,7 @@ const { pathToFileURL } = require("node:url");
|
|
|
17
17
|
const { SHARE_ENV, Worker } = require("node:worker_threads");
|
|
18
18
|
const { version } = require("../../package.json");
|
|
19
19
|
const { defaultConfig } = require("../config/default-config");
|
|
20
|
+
const timing = require("../linter/timing");
|
|
20
21
|
|
|
21
22
|
const {
|
|
22
23
|
createDebug,
|
|
@@ -503,6 +504,11 @@ async function runWorkers(
|
|
|
503
504
|
worstNetLintingRatio,
|
|
504
505
|
netLintingRatio,
|
|
505
506
|
);
|
|
507
|
+
|
|
508
|
+
if (timing.enabled && indexedResults.timings) {
|
|
509
|
+
timing.mergeData(indexedResults.timings);
|
|
510
|
+
}
|
|
511
|
+
|
|
506
512
|
for (const result of indexedResults) {
|
|
507
513
|
const { index } = result;
|
|
508
514
|
delete result.index;
|
|
@@ -522,7 +528,17 @@ async function runWorkers(
|
|
|
522
528
|
for (let index = 0; index < workerCount; ++index) {
|
|
523
529
|
promises[index] = new Promise(workerExecutor);
|
|
524
530
|
}
|
|
525
|
-
|
|
531
|
+
|
|
532
|
+
try {
|
|
533
|
+
await Promise.all(promises);
|
|
534
|
+
} catch (error) {
|
|
535
|
+
/*
|
|
536
|
+
* If any worker fails, suppress timing display in the main thread
|
|
537
|
+
* to avoid printing partial or misleading timing output.
|
|
538
|
+
*/
|
|
539
|
+
timing.disableDisplay();
|
|
540
|
+
throw error;
|
|
541
|
+
}
|
|
526
542
|
|
|
527
543
|
if (worstNetLintingRatio < LOW_NET_LINTING_RATIO) {
|
|
528
544
|
warnOnLowNetLintingRatio();
|
package/lib/eslint/worker.js
CHANGED
|
@@ -29,6 +29,7 @@ const {
|
|
|
29
29
|
processOptions,
|
|
30
30
|
} = require("./eslint-helpers");
|
|
31
31
|
const { WarningService } = require("../services/warning-service");
|
|
32
|
+
const timing = require("../linter/timing");
|
|
32
33
|
|
|
33
34
|
const depsLoadedTime = hrtimeBigint();
|
|
34
35
|
|
|
@@ -39,7 +40,7 @@ const depsLoadedTime = hrtimeBigint();
|
|
|
39
40
|
/** @typedef {import("../types").ESLint.LintResult} LintResult */
|
|
40
41
|
/** @typedef {import("../types").ESLint.Options} ESLintOptions */
|
|
41
42
|
/** @typedef {LintResult & { index?: number; }} IndexedLintResult */
|
|
42
|
-
/** @typedef {IndexedLintResult[] & { netLintingDuration: bigint; }} WorkerLintResults */
|
|
43
|
+
/** @typedef {IndexedLintResult[] & { netLintingDuration: bigint; timings?: Record<string, number>; }} WorkerLintResults */
|
|
43
44
|
/**
|
|
44
45
|
* @typedef {Object} WorkerData - Data passed to the worker thread.
|
|
45
46
|
* @property {ESLintOptions | string} eslintOptionsOrURL - The unprocessed ESLint options or the URL of the options module.
|
|
@@ -57,6 +58,12 @@ const debug = createDebug(`eslint:worker:thread-${threadId}`);
|
|
|
57
58
|
// Main
|
|
58
59
|
//------------------------------------------------------------------------------
|
|
59
60
|
|
|
61
|
+
/*
|
|
62
|
+
* Prevent timing module from printing profiling output from worker threads.
|
|
63
|
+
* The main thread is responsible for displaying any aggregated timings.
|
|
64
|
+
*/
|
|
65
|
+
timing.disableDisplay();
|
|
66
|
+
|
|
60
67
|
debug("Dependencies loaded in %t", depsLoadedTime - startTime);
|
|
61
68
|
|
|
62
69
|
(async () => {
|
|
@@ -158,5 +165,9 @@ debug("Dependencies loaded in %t", depsLoadedTime - startTime);
|
|
|
158
165
|
indexedResults.netLintingDuration =
|
|
159
166
|
lintingDuration - loadConfigTotalDuration - readFileCounter.duration;
|
|
160
167
|
|
|
168
|
+
if (timing.enabled) {
|
|
169
|
+
indexedResults.timings = timing.getData();
|
|
170
|
+
}
|
|
171
|
+
|
|
161
172
|
parentPort.postMessage(indexedResults);
|
|
162
173
|
})();
|
|
@@ -1303,7 +1303,7 @@ class SourceCode extends TokenStore {
|
|
|
1303
1303
|
new VisitNodeStep({
|
|
1304
1304
|
target: node,
|
|
1305
1305
|
phase: 1,
|
|
1306
|
-
args: [node
|
|
1306
|
+
args: [node],
|
|
1307
1307
|
}),
|
|
1308
1308
|
);
|
|
1309
1309
|
},
|
|
@@ -1312,7 +1312,7 @@ class SourceCode extends TokenStore {
|
|
|
1312
1312
|
new VisitNodeStep({
|
|
1313
1313
|
target: node,
|
|
1314
1314
|
phase: 2,
|
|
1315
|
-
args: [node
|
|
1315
|
+
args: [node],
|
|
1316
1316
|
}),
|
|
1317
1317
|
);
|
|
1318
1318
|
},
|
|
@@ -426,7 +426,7 @@ function validateSuggestions(suggest, messages) {
|
|
|
426
426
|
|
|
427
427
|
if (typeof suggestion.fix !== "function") {
|
|
428
428
|
throw new TypeError(
|
|
429
|
-
`context.report() called with a suggest option without a fix function. See: ${suggestion}`,
|
|
429
|
+
`context.report() called with a suggest option without a fix function. See: ${JSON.stringify(suggestion, null, 2)}`,
|
|
430
430
|
);
|
|
431
431
|
}
|
|
432
432
|
});
|
|
@@ -17,8 +17,9 @@ const vk = require("eslint-visitor-keys");
|
|
|
17
17
|
//-----------------------------------------------------------------------------
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* @import { ESQueryParsedSelector } from "./esquery.js";
|
|
21
20
|
* @import { Language, SourceCode } from "@eslint/core";
|
|
21
|
+
* @import { ESQueryOptions } from "esquery";
|
|
22
|
+
* @import { ESQueryParsedSelector } from "./esquery.js";
|
|
22
23
|
* @import { SourceCodeVisitor } from "./source-code-visitor.js";
|
|
23
24
|
*/
|
|
24
25
|
|
|
@@ -47,11 +48,10 @@ class ESQueryHelper {
|
|
|
47
48
|
* Creates a new instance.
|
|
48
49
|
* @param {SourceCodeVisitor} visitor The visitor containing the functions to call.
|
|
49
50
|
* @param {ESQueryOptions} esqueryOptions `esquery` options for traversing custom nodes.
|
|
50
|
-
* @returns {NodeEventGenerator} new instance
|
|
51
51
|
*/
|
|
52
52
|
constructor(visitor, esqueryOptions) {
|
|
53
53
|
/**
|
|
54
|
-
* The
|
|
54
|
+
* The visitor to use during traversal.
|
|
55
55
|
* @type {SourceCodeVisitor}
|
|
56
56
|
*/
|
|
57
57
|
this.visitor = visitor;
|
|
@@ -288,7 +288,10 @@ class SourceCodeTraverser {
|
|
|
288
288
|
false,
|
|
289
289
|
)
|
|
290
290
|
.forEach(selector => {
|
|
291
|
-
visitor.callSync(
|
|
291
|
+
visitor.callSync(
|
|
292
|
+
selector,
|
|
293
|
+
...(step.args ?? [step.target]),
|
|
294
|
+
);
|
|
292
295
|
});
|
|
293
296
|
currentAncestry.unshift(step.target);
|
|
294
297
|
} else {
|
|
@@ -300,7 +303,10 @@ class SourceCodeTraverser {
|
|
|
300
303
|
true,
|
|
301
304
|
)
|
|
302
305
|
.forEach(selector => {
|
|
303
|
-
visitor.callSync(
|
|
306
|
+
visitor.callSync(
|
|
307
|
+
selector,
|
|
308
|
+
...(step.args ?? [step.target]),
|
|
309
|
+
);
|
|
304
310
|
});
|
|
305
311
|
}
|
|
306
312
|
} catch (err) {
|
package/lib/linter/timing.js
CHANGED
|
@@ -131,6 +131,7 @@ function display(data) {
|
|
|
131
131
|
/* c8 ignore next */
|
|
132
132
|
module.exports = (function () {
|
|
133
133
|
const data = Object.create(null);
|
|
134
|
+
let displayEnabled = true;
|
|
134
135
|
|
|
135
136
|
/**
|
|
136
137
|
* Time the run
|
|
@@ -158,9 +159,42 @@ module.exports = (function () {
|
|
|
158
159
|
};
|
|
159
160
|
}
|
|
160
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Returns a shallow copy of the collected timings data.
|
|
164
|
+
* @returns {Record<string, number>} mapping of ruleId to total time in ms
|
|
165
|
+
*/
|
|
166
|
+
function getData() {
|
|
167
|
+
return { ...data };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Merges rule timing totals collected elsewhere into this process' totals.
|
|
172
|
+
* @param {Record<string, number>} dataToMerge mapping of ruleId to total time in ms
|
|
173
|
+
* @returns {void}
|
|
174
|
+
*/
|
|
175
|
+
function mergeData(dataToMerge) {
|
|
176
|
+
for (const [key, value] of Object.entries(dataToMerge)) {
|
|
177
|
+
if (typeof data[key] === "undefined") {
|
|
178
|
+
data[key] = 0;
|
|
179
|
+
}
|
|
180
|
+
data[key] += value;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Disables printing of timing data on process exit.
|
|
186
|
+
* Intended for worker threads or non-main contexts.
|
|
187
|
+
* @returns {void}
|
|
188
|
+
*/
|
|
189
|
+
function disableDisplay() {
|
|
190
|
+
displayEnabled = false;
|
|
191
|
+
}
|
|
192
|
+
|
|
161
193
|
if (enabled) {
|
|
162
194
|
process.on("exit", () => {
|
|
163
|
-
|
|
195
|
+
if (displayEnabled && Object.keys(data).length > 0) {
|
|
196
|
+
display(data);
|
|
197
|
+
}
|
|
164
198
|
});
|
|
165
199
|
}
|
|
166
200
|
|
|
@@ -168,5 +202,8 @@ module.exports = (function () {
|
|
|
168
202
|
time,
|
|
169
203
|
enabled,
|
|
170
204
|
getListSize,
|
|
205
|
+
getData,
|
|
206
|
+
mergeData,
|
|
207
|
+
disableDisplay,
|
|
171
208
|
};
|
|
172
209
|
})();
|
package/lib/rules/complexity.js
CHANGED
|
@@ -68,6 +68,7 @@ module.exports = {
|
|
|
68
68
|
},
|
|
69
69
|
|
|
70
70
|
create(context) {
|
|
71
|
+
const sourceCode = context.sourceCode;
|
|
71
72
|
const option = context.options[0];
|
|
72
73
|
let threshold = THRESHOLD_DEFAULT;
|
|
73
74
|
let VARIANT = "classic";
|
|
@@ -177,12 +178,10 @@ module.exports = {
|
|
|
177
178
|
name = "class field initializer";
|
|
178
179
|
} else if (codePath.origin === "class-static-block") {
|
|
179
180
|
name = "class static block";
|
|
181
|
+
loc = sourceCode.getFirstToken(node).loc;
|
|
180
182
|
} else {
|
|
181
183
|
name = astUtils.getFunctionNameWithKind(node);
|
|
182
|
-
loc = astUtils.getFunctionHeadLoc(
|
|
183
|
-
node,
|
|
184
|
-
context.sourceCode,
|
|
185
|
-
);
|
|
184
|
+
loc = astUtils.getFunctionHeadLoc(node, sourceCode);
|
|
186
185
|
}
|
|
187
186
|
|
|
188
187
|
context.report({
|
|
@@ -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
|
//------------------------------------------------------------------------------
|
|
@@ -58,10 +64,15 @@ module.exports = {
|
|
|
58
64
|
|
|
59
65
|
// Checks and reports duplications.
|
|
60
66
|
const defs = variable.defs.filter(isParameter);
|
|
67
|
+
const loc = {
|
|
68
|
+
start: astUtils.getOpeningParenOfParams(node, sourceCode)
|
|
69
|
+
.loc.start,
|
|
70
|
+
end: sourceCode.getTokenBefore(node.body).loc.end,
|
|
71
|
+
};
|
|
61
72
|
|
|
62
73
|
if (defs.length >= 2) {
|
|
63
74
|
context.report({
|
|
64
|
-
|
|
75
|
+
loc,
|
|
65
76
|
messageId: "unexpected",
|
|
66
77
|
data: { name: variable.name },
|
|
67
78
|
});
|
|
@@ -19,6 +19,86 @@ const OPTIONS = {
|
|
|
19
19
|
//------------------------------------------------------------------------------
|
|
20
20
|
const astUtils = require("./utils/ast-utils");
|
|
21
21
|
|
|
22
|
+
//--------------------------------------------------------------------------
|
|
23
|
+
// Helpers
|
|
24
|
+
//--------------------------------------------------------------------------
|
|
25
|
+
const CTOR_PREFIX_REGEX = /[^_$0-9]/u;
|
|
26
|
+
const JSDOC_COMMENT_REGEX = /^\s*\*/u;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Determines if the first character of the name is a capital letter.
|
|
30
|
+
* @param {string} name The name of the node to evaluate.
|
|
31
|
+
* @returns {boolean} True if the first character of the property name is a capital letter, false if not.
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
34
|
+
function isConstructor(name) {
|
|
35
|
+
const match = CTOR_PREFIX_REGEX.exec(name);
|
|
36
|
+
|
|
37
|
+
// Not a constructor if name has no characters apart from '_', '$' and digits e.g. '_', '$$', '_8'
|
|
38
|
+
if (!match) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const firstChar = name.charAt(match.index);
|
|
43
|
+
|
|
44
|
+
return firstChar === firstChar.toUpperCase();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Determines if the property can have a shorthand form.
|
|
49
|
+
* @param {ASTNode} property Property AST node
|
|
50
|
+
* @returns {boolean} True if the property can have a shorthand form
|
|
51
|
+
* @private
|
|
52
|
+
*/
|
|
53
|
+
function canHaveShorthand(property) {
|
|
54
|
+
return (
|
|
55
|
+
property.kind !== "set" &&
|
|
56
|
+
property.kind !== "get" &&
|
|
57
|
+
property.type !== "SpreadElement" &&
|
|
58
|
+
property.type !== "SpreadProperty" &&
|
|
59
|
+
property.type !== "ExperimentalSpreadProperty"
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Checks whether a node is a string literal.
|
|
65
|
+
* @param {ASTNode} node Any AST node.
|
|
66
|
+
* @returns {boolean} `true` if it is a string literal.
|
|
67
|
+
*/
|
|
68
|
+
function isStringLiteral(node) {
|
|
69
|
+
return node.type === "Literal" && typeof node.value === "string";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Determines if the property is a shorthand or not.
|
|
74
|
+
* @param {ASTNode} property Property AST node
|
|
75
|
+
* @returns {boolean} True if the property is considered shorthand, false if not.
|
|
76
|
+
* @private
|
|
77
|
+
*/
|
|
78
|
+
function isShorthand(property) {
|
|
79
|
+
// property.method is true when `{a(){}}`.
|
|
80
|
+
return property.shorthand || property.method;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Determines if the property's key and method or value are named equally.
|
|
85
|
+
* @param {ASTNode} property Property AST node
|
|
86
|
+
* @returns {boolean} True if the key and value are named equally, false if not.
|
|
87
|
+
* @private
|
|
88
|
+
*/
|
|
89
|
+
function isRedundant(property) {
|
|
90
|
+
const value = property.value;
|
|
91
|
+
|
|
92
|
+
if (value.type === "FunctionExpression") {
|
|
93
|
+
return !value.id; // Only anonymous should be shorthand method.
|
|
94
|
+
}
|
|
95
|
+
if (value.type === "Identifier") {
|
|
96
|
+
return astUtils.getStaticPropertyName(property) === value.name;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
22
102
|
//------------------------------------------------------------------------------
|
|
23
103
|
// Rule Definition
|
|
24
104
|
//------------------------------------------------------------------------------
|
|
@@ -139,86 +219,6 @@ module.exports = {
|
|
|
139
219
|
const AVOID_EXPLICIT_RETURN_ARROWS = !!PARAMS.avoidExplicitReturnArrows;
|
|
140
220
|
const sourceCode = context.sourceCode;
|
|
141
221
|
|
|
142
|
-
//--------------------------------------------------------------------------
|
|
143
|
-
// Helpers
|
|
144
|
-
//--------------------------------------------------------------------------
|
|
145
|
-
|
|
146
|
-
const CTOR_PREFIX_REGEX = /[^_$0-9]/u;
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Determines if the first character of the name is a capital letter.
|
|
150
|
-
* @param {string} name The name of the node to evaluate.
|
|
151
|
-
* @returns {boolean} True if the first character of the property name is a capital letter, false if not.
|
|
152
|
-
* @private
|
|
153
|
-
*/
|
|
154
|
-
function isConstructor(name) {
|
|
155
|
-
const match = CTOR_PREFIX_REGEX.exec(name);
|
|
156
|
-
|
|
157
|
-
// Not a constructor if name has no characters apart from '_', '$' and digits e.g. '_', '$$', '_8'
|
|
158
|
-
if (!match) {
|
|
159
|
-
return false;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const firstChar = name.charAt(match.index);
|
|
163
|
-
|
|
164
|
-
return firstChar === firstChar.toUpperCase();
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Determines if the property can have a shorthand form.
|
|
169
|
-
* @param {ASTNode} property Property AST node
|
|
170
|
-
* @returns {boolean} True if the property can have a shorthand form
|
|
171
|
-
* @private
|
|
172
|
-
*/
|
|
173
|
-
function canHaveShorthand(property) {
|
|
174
|
-
return (
|
|
175
|
-
property.kind !== "set" &&
|
|
176
|
-
property.kind !== "get" &&
|
|
177
|
-
property.type !== "SpreadElement" &&
|
|
178
|
-
property.type !== "SpreadProperty" &&
|
|
179
|
-
property.type !== "ExperimentalSpreadProperty"
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Checks whether a node is a string literal.
|
|
185
|
-
* @param {ASTNode} node Any AST node.
|
|
186
|
-
* @returns {boolean} `true` if it is a string literal.
|
|
187
|
-
*/
|
|
188
|
-
function isStringLiteral(node) {
|
|
189
|
-
return node.type === "Literal" && typeof node.value === "string";
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Determines if the property is a shorthand or not.
|
|
194
|
-
* @param {ASTNode} property Property AST node
|
|
195
|
-
* @returns {boolean} True if the property is considered shorthand, false if not.
|
|
196
|
-
* @private
|
|
197
|
-
*/
|
|
198
|
-
function isShorthand(property) {
|
|
199
|
-
// property.method is true when `{a(){}}`.
|
|
200
|
-
return property.shorthand || property.method;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Determines if the property's key and method or value are named equally.
|
|
205
|
-
* @param {ASTNode} property Property AST node
|
|
206
|
-
* @returns {boolean} True if the key and value are named equally, false if not.
|
|
207
|
-
* @private
|
|
208
|
-
*/
|
|
209
|
-
function isRedundant(property) {
|
|
210
|
-
const value = property.value;
|
|
211
|
-
|
|
212
|
-
if (value.type === "FunctionExpression") {
|
|
213
|
-
return !value.id; // Only anonymous should be shorthand method.
|
|
214
|
-
}
|
|
215
|
-
if (value.type === "Identifier") {
|
|
216
|
-
return astUtils.getStaticPropertyName(property) === value.name;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return false;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
222
|
/**
|
|
223
223
|
* Ensures that an object's properties are consistently shorthand, or not shorthand at all.
|
|
224
224
|
* @param {ASTNode} node Property AST node
|
|
@@ -582,6 +582,19 @@ module.exports = {
|
|
|
582
582
|
node.key.name === node.value.name &&
|
|
583
583
|
APPLY_TO_PROPS
|
|
584
584
|
) {
|
|
585
|
+
// Skip if there are JSDoc comments inside the property (e.g., JSDoc type annotations)
|
|
586
|
+
const comments = sourceCode.getCommentsInside(node);
|
|
587
|
+
if (
|
|
588
|
+
comments.some(
|
|
589
|
+
comment =>
|
|
590
|
+
comment.type === "Block" &&
|
|
591
|
+
JSDOC_COMMENT_REGEX.test(comment.value) &&
|
|
592
|
+
comment.value.includes("@type"),
|
|
593
|
+
)
|
|
594
|
+
) {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
|
|
585
598
|
// {x: x} should be written as {x}
|
|
586
599
|
context.report({
|
|
587
600
|
node,
|
|
@@ -606,6 +619,18 @@ module.exports = {
|
|
|
606
619
|
return;
|
|
607
620
|
}
|
|
608
621
|
|
|
622
|
+
const comments = sourceCode.getCommentsInside(node);
|
|
623
|
+
if (
|
|
624
|
+
comments.some(
|
|
625
|
+
comment =>
|
|
626
|
+
comment.type === "Block" &&
|
|
627
|
+
comment.value.startsWith("*") &&
|
|
628
|
+
comment.value.includes("@type"),
|
|
629
|
+
)
|
|
630
|
+
) {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
|
|
609
634
|
// {"x": x} should be written as {x}
|
|
610
635
|
context.report({
|
|
611
636
|
node,
|