eslint 10.1.0 → 10.2.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 +3 -1
- package/conf/globals.js +1 -0
- package/lib/config/config.js +129 -0
- package/lib/config/flat-config-schema.js +9 -6
- package/lib/linter/code-path-analysis/code-path-analyzer.js +6 -3
- package/lib/linter/code-path-analysis/code-path-state.js +21 -5
- package/lib/linter/code-path-analysis/code-path.js +6 -4
- package/lib/linter/linter.js +16 -0
- package/lib/rules/no-async-promise-executor.js +7 -1
- package/lib/rules/no-obj-calls.js +8 -1
- package/lib/rules/no-shadow.js +1 -1
- package/lib/rules/no-unmodified-loop-condition.js +30 -19
- package/messages/rule-unsupported-language.js +18 -0
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -218,6 +218,8 @@ Packages that are controlled by the ESLint team and have no external dependencie
|
|
|
218
218
|
|
|
219
219
|
For external packages, we don't use `require(esm)` because a package could add a top-level `await` and thus break ESLint. We can use an external ESM-only package only in case it is needed only in asynchronous code, in which case it can be loaded using dynamic `import()`.
|
|
220
220
|
|
|
221
|
+
These policies don't apply to packages intended for our own usage only, such as `eslint-config-eslint`.
|
|
222
|
+
|
|
221
223
|
## License
|
|
222
224
|
|
|
223
225
|
MIT License
|
|
@@ -358,7 +360,7 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
|
|
|
358
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>
|
|
359
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>
|
|
360
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>
|
|
361
|
-
<p><a href="https://
|
|
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>
|
|
362
364
|
<h3>Technology Sponsors</h3>
|
|
363
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.
|
|
364
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>
|
package/conf/globals.js
CHANGED
package/lib/config/config.js
CHANGED
|
@@ -228,6 +228,76 @@ function splitPluginIdentifier(identifier) {
|
|
|
228
228
|
};
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
+
/**
|
|
232
|
+
* Normalizes a language name by replacing the built-in `@/` plugin prefix with `js/`.
|
|
233
|
+
* @param {string} languageName The language name to normalize.
|
|
234
|
+
* @returns {string} The normalized language name.
|
|
235
|
+
*/
|
|
236
|
+
function normalizeLanguageName(languageName) {
|
|
237
|
+
return languageName.startsWith("@/")
|
|
238
|
+
? `js/${languageName.slice(2)}`
|
|
239
|
+
: languageName;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Checks if a rule's `meta.languages` supports the given language.
|
|
244
|
+
* @param {Array<string>|undefined} ruleLangs The rule's `meta.languages` array.
|
|
245
|
+
* @param {string} configLanguageName The normalized language name from the config (e.g., "js/js").
|
|
246
|
+
* @param {Array<string>} validPluginNames The valid plugin name aliases for the config's plugin
|
|
247
|
+
* (normalized plugin name plus its `meta.namespace` if defined).
|
|
248
|
+
* @returns {boolean} True if the rule supports the language, false otherwise.
|
|
249
|
+
*/
|
|
250
|
+
function doesRuleSupportLanguage(
|
|
251
|
+
ruleLangs,
|
|
252
|
+
configLanguageName,
|
|
253
|
+
validPluginNames,
|
|
254
|
+
) {
|
|
255
|
+
// If no languages specified, works with all languages (backward compatible)
|
|
256
|
+
if (!ruleLangs) {
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const { objectName: configLangPart } =
|
|
261
|
+
splitPluginIdentifier(configLanguageName);
|
|
262
|
+
|
|
263
|
+
for (const langEntry of ruleLangs) {
|
|
264
|
+
// Skip non-string entries
|
|
265
|
+
if (typeof langEntry !== "string") {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// "*" matches any language
|
|
270
|
+
if (langEntry === "*") {
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Direct match
|
|
275
|
+
if (langEntry === configLanguageName) {
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const { pluginName: rulePluginPart, objectName: ruleLangPart } =
|
|
280
|
+
splitPluginIdentifier(langEntry);
|
|
281
|
+
|
|
282
|
+
// "plugin/*" wildcard - matches any language from that plugin (by name or namespace)
|
|
283
|
+
if (ruleLangPart === "*") {
|
|
284
|
+
if (validPluginNames.includes(rulePluginPart)) {
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
} else {
|
|
288
|
+
// Match by plugin name or namespace, with exact language part
|
|
289
|
+
if (
|
|
290
|
+
validPluginNames.includes(rulePluginPart) &&
|
|
291
|
+
ruleLangPart === configLangPart
|
|
292
|
+
) {
|
|
293
|
+
return true;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
|
|
231
301
|
/**
|
|
232
302
|
* Returns the name of an object in the config by reading its `meta` key.
|
|
233
303
|
* @param {Object} object The object to check.
|
|
@@ -572,12 +642,30 @@ class Config {
|
|
|
572
642
|
* @throws {TypeError} If the rulesConfig is not provided or is invalid.
|
|
573
643
|
* @throws {InvalidRuleOptionsSchemaError} If a rule's `meta.schema` is invalid.
|
|
574
644
|
* @throws {TypeError} If a rule is not found in the plugins.
|
|
645
|
+
* @throws {TypeError} If a rule does not support the current language.
|
|
575
646
|
*/
|
|
576
647
|
validateRulesConfig(rulesConfig) {
|
|
577
648
|
if (!rulesConfig) {
|
|
578
649
|
throw new TypeError("Config is required for validation.");
|
|
579
650
|
}
|
|
580
651
|
|
|
652
|
+
// Normalize "@/" prefix to "js/" for matching and user-facing messages
|
|
653
|
+
const normalizedLanguageName = normalizeLanguageName(
|
|
654
|
+
this.#languageName,
|
|
655
|
+
);
|
|
656
|
+
|
|
657
|
+
// Compute valid plugin name aliases for the config's language plugin once
|
|
658
|
+
const { pluginName: configPluginName } = splitPluginIdentifier(
|
|
659
|
+
normalizedLanguageName,
|
|
660
|
+
);
|
|
661
|
+
const configPlugin =
|
|
662
|
+
this.plugins[configPluginName] ??
|
|
663
|
+
(configPluginName === "js" ? this.plugins["@"] : void 0);
|
|
664
|
+
const validPluginNames = configPlugin?.meta?.namespace
|
|
665
|
+
? [configPluginName, configPlugin.meta.namespace]
|
|
666
|
+
: [configPluginName];
|
|
667
|
+
const unsupportedLanguageRules = [];
|
|
668
|
+
|
|
581
669
|
for (const [ruleId, ruleOptions] of Object.entries(rulesConfig)) {
|
|
582
670
|
// check for edge case
|
|
583
671
|
if (ruleId === "__proto__") {
|
|
@@ -603,6 +691,34 @@ class Config {
|
|
|
603
691
|
throwRuleNotFoundError(parseRuleId(ruleId), this);
|
|
604
692
|
}
|
|
605
693
|
|
|
694
|
+
// Validate meta.languages structure if present (only for enabled rules)
|
|
695
|
+
if (rule.meta?.languages !== void 0) {
|
|
696
|
+
if (!Array.isArray(rule.meta.languages)) {
|
|
697
|
+
throw new TypeError(
|
|
698
|
+
`Key "rules": Key "${ruleId}": Key "meta": Key "languages": Expected an array.`,
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
for (const lang of rule.meta.languages) {
|
|
703
|
+
if (typeof lang !== "string") {
|
|
704
|
+
throw new TypeError(
|
|
705
|
+
`Key "rules": Key "${ruleId}": Key "meta": Key "languages": Expected each element to be a string.`,
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// Check if the rule supports the current language
|
|
712
|
+
if (
|
|
713
|
+
!doesRuleSupportLanguage(
|
|
714
|
+
rule.meta?.languages,
|
|
715
|
+
normalizedLanguageName,
|
|
716
|
+
validPluginNames,
|
|
717
|
+
)
|
|
718
|
+
) {
|
|
719
|
+
unsupportedLanguageRules.push(ruleId);
|
|
720
|
+
}
|
|
721
|
+
|
|
606
722
|
const validateRule = getOrCreateValidator(rule, ruleId);
|
|
607
723
|
|
|
608
724
|
if (validateRule) {
|
|
@@ -634,6 +750,19 @@ class Config {
|
|
|
634
750
|
}
|
|
635
751
|
}
|
|
636
752
|
}
|
|
753
|
+
|
|
754
|
+
if (unsupportedLanguageRules.length > 0) {
|
|
755
|
+
const error = new TypeError(
|
|
756
|
+
`Key "rules": The following rules do not support the language "${normalizedLanguageName}":\n${unsupportedLanguageRules.map(ruleId => `\t- "${ruleId}"`).join("\n")}`,
|
|
757
|
+
);
|
|
758
|
+
|
|
759
|
+
error.messageTemplate = "rule-unsupported-language";
|
|
760
|
+
error.messageData = {
|
|
761
|
+
ruleIds: unsupportedLanguageRules,
|
|
762
|
+
language: normalizedLanguageName,
|
|
763
|
+
};
|
|
764
|
+
throw error;
|
|
765
|
+
}
|
|
637
766
|
}
|
|
638
767
|
|
|
639
768
|
/**
|
|
@@ -224,15 +224,16 @@ function assertIsRuleSeverity(ruleId, value) {
|
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
/**
|
|
227
|
-
* Validates that a given string
|
|
227
|
+
* Validates that a given string matches the "pluginName/memberPlaceholder" pattern.
|
|
228
228
|
* @param {string} value The string to check.
|
|
229
|
+
* @param {string} memberPlaceholder The placeholder for the member portion of the expected format in the error message.
|
|
229
230
|
* @returns {void}
|
|
230
|
-
* @throws {TypeError} If the string
|
|
231
|
+
* @throws {TypeError} If the string doesn't match the expected pattern.
|
|
231
232
|
*/
|
|
232
|
-
function assertIsPluginMemberName(value) {
|
|
233
|
+
function assertIsPluginMemberName(value, memberPlaceholder) {
|
|
233
234
|
if (!/[\w\-@$]+(?:\/[\w\-$]+)+$/iu.test(value)) {
|
|
234
235
|
throw new TypeError(
|
|
235
|
-
`Expected string in the form "pluginName
|
|
236
|
+
`Expected string in the form "pluginName/${memberPlaceholder}" but found "${value}".`,
|
|
236
237
|
);
|
|
237
238
|
}
|
|
238
239
|
}
|
|
@@ -375,7 +376,9 @@ const languageOptionsSchema = {
|
|
|
375
376
|
/** @type {ObjectPropertySchema} */
|
|
376
377
|
const languageSchema = {
|
|
377
378
|
merge: "replace",
|
|
378
|
-
validate
|
|
379
|
+
validate(value) {
|
|
380
|
+
assertIsPluginMemberName(value, "languageName");
|
|
381
|
+
},
|
|
379
382
|
};
|
|
380
383
|
|
|
381
384
|
/** @type {ObjectPropertySchema} */
|
|
@@ -430,7 +433,7 @@ const processorSchema = {
|
|
|
430
433
|
merge: "replace",
|
|
431
434
|
validate(value) {
|
|
432
435
|
if (typeof value === "string") {
|
|
433
|
-
assertIsPluginMemberName(value);
|
|
436
|
+
assertIsPluginMemberName(value, "processorName");
|
|
434
437
|
} else if (value && typeof value === "object") {
|
|
435
438
|
if (
|
|
436
439
|
typeof value.preprocess !== "function" ||
|
|
@@ -31,7 +31,7 @@ function isCaseNode(node) {
|
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Checks if a given node appears as the value of a PropertyDefinition node.
|
|
34
|
-
* @param {ASTNode} node
|
|
34
|
+
* @param {ASTNode} node The node to check.
|
|
35
35
|
* @returns {boolean} `true` if the node is a PropertyDefinition value,
|
|
36
36
|
* false if not.
|
|
37
37
|
*/
|
|
@@ -504,7 +504,7 @@ function processCodePathToEnter(analyzer, node) {
|
|
|
504
504
|
|
|
505
505
|
case "SwitchCase":
|
|
506
506
|
/*
|
|
507
|
-
* Fork if this node is after the
|
|
507
|
+
* Fork if this node is after the 1st node in `cases`.
|
|
508
508
|
* It's similar to `else` blocks.
|
|
509
509
|
* The next `test` node is processed in this path.
|
|
510
510
|
*/
|
|
@@ -626,10 +626,13 @@ function processCodePathToExit(analyzer, node) {
|
|
|
626
626
|
case "ImportExpression":
|
|
627
627
|
case "MemberExpression":
|
|
628
628
|
case "NewExpression":
|
|
629
|
-
case "YieldExpression":
|
|
630
629
|
state.makeFirstThrowablePathInTryBlock();
|
|
631
630
|
break;
|
|
632
631
|
|
|
632
|
+
case "YieldExpression":
|
|
633
|
+
state.makeYield();
|
|
634
|
+
break;
|
|
635
|
+
|
|
633
636
|
case "WhileStatement":
|
|
634
637
|
case "DoWhileStatement":
|
|
635
638
|
case "ForStatement":
|
|
@@ -569,11 +569,10 @@ class TryContext {
|
|
|
569
569
|
this.position = "try";
|
|
570
570
|
|
|
571
571
|
/**
|
|
572
|
-
* If the `try` statement has a `finally` block, this affects how
|
|
573
|
-
*
|
|
574
|
-
*
|
|
575
|
-
* `
|
|
576
|
-
* to track it.
|
|
572
|
+
* If the `try` statement has a `finally` block, this affects how return-like
|
|
573
|
+
* leaving paths behave in the `try` block. Without `finally`, they behave as
|
|
574
|
+
* usual and don't require a fork; with `finally`, they fork into the
|
|
575
|
+
* `finally` block, so we need a fork context to track them.
|
|
577
576
|
* @type {ForkContext|null}
|
|
578
577
|
*/
|
|
579
578
|
this.returnedForkContext = hasFinalizer
|
|
@@ -1755,6 +1754,23 @@ class CodePathState {
|
|
|
1755
1754
|
this.forkContext.add(segments);
|
|
1756
1755
|
}
|
|
1757
1756
|
|
|
1757
|
+
/**
|
|
1758
|
+
* Records abrupt resumption paths from a suspended `yield` expression,
|
|
1759
|
+
* then splits normal post-`yield` continuation into a fresh segment.
|
|
1760
|
+
* @returns {void}
|
|
1761
|
+
*/
|
|
1762
|
+
makeYield() {
|
|
1763
|
+
const forkContext = this.forkContext;
|
|
1764
|
+
const leavingSegments = forkContext.head;
|
|
1765
|
+
|
|
1766
|
+
if (forkContext.reachable) {
|
|
1767
|
+
getReturnContext(this).returnedForkContext.add(leavingSegments);
|
|
1768
|
+
getThrowContext(this).thrownForkContext.add(leavingSegments);
|
|
1769
|
+
|
|
1770
|
+
forkContext.replaceHead(forkContext.makeNext(-1, -1));
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1758
1774
|
/**
|
|
1759
1775
|
* Makes a code path segment from the first throwable node to the `catch`
|
|
1760
1776
|
* block or the `finally` block.
|
|
@@ -100,9 +100,10 @@ class CodePath {
|
|
|
100
100
|
* Final code path segments that represent normal completion of the code path.
|
|
101
101
|
* For functions, this means both explicit `return` statements and implicit returns,
|
|
102
102
|
* such as the last reachable segment in a function that does not have an
|
|
103
|
-
* explicit `return` as this implicitly returns `undefined
|
|
104
|
-
*
|
|
105
|
-
*
|
|
103
|
+
* explicit `return` as this implicitly returns `undefined`, as well as
|
|
104
|
+
* return-like exits from suspended `yield` expressions. For scripts, modules,
|
|
105
|
+
* class field initializers, and class static blocks, this means all lines of
|
|
106
|
+
* code have been executed.
|
|
106
107
|
* These segments are also present in `finalSegments`.
|
|
107
108
|
* This is a passthrough to the underlying `CodePathState`.
|
|
108
109
|
* @type {CodePathSegment[]}
|
|
@@ -112,7 +113,8 @@ class CodePath {
|
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
/**
|
|
115
|
-
* Final code path segments that represent `throw` statements
|
|
116
|
+
* Final code path segments that represent `throw` statements and throw-like
|
|
117
|
+
* exits from suspended `yield` expressions.
|
|
116
118
|
* This is a passthrough to the underlying `CodePathState`.
|
|
117
119
|
* These segments are also present in `finalSegments`.
|
|
118
120
|
* @type {CodePathSegment[]}
|
package/lib/linter/linter.js
CHANGED
|
@@ -1202,6 +1202,22 @@ class Linter {
|
|
|
1202
1202
|
throw err;
|
|
1203
1203
|
}
|
|
1204
1204
|
|
|
1205
|
+
/*
|
|
1206
|
+
* If the rule does not support the current language, report a
|
|
1207
|
+
* specific, actionable error message.
|
|
1208
|
+
*/
|
|
1209
|
+
if (
|
|
1210
|
+
err.messageTemplate ===
|
|
1211
|
+
"rule-unsupported-language"
|
|
1212
|
+
) {
|
|
1213
|
+
report.addError({
|
|
1214
|
+
ruleId,
|
|
1215
|
+
message: `Inline configuration for rule "${ruleId}" is invalid:\n\tRule does not support the language "${err.messageData.language}". Use a config block with "files" to apply the rule only to supported files, or disable it.\n`,
|
|
1216
|
+
loc,
|
|
1217
|
+
});
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1205
1221
|
let baseMessage = err.message
|
|
1206
1222
|
.slice(
|
|
1207
1223
|
err.message.startsWith('Key "rules":')
|
|
@@ -28,12 +28,18 @@ module.exports = {
|
|
|
28
28
|
},
|
|
29
29
|
|
|
30
30
|
create(context) {
|
|
31
|
+
const sourceCode = context.sourceCode;
|
|
32
|
+
|
|
31
33
|
return {
|
|
32
34
|
"NewExpression[callee.name='Promise'][arguments.0.async=true]"(
|
|
33
35
|
node,
|
|
34
36
|
) {
|
|
37
|
+
if (!sourceCode.isGlobalReference(node.callee)) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
35
41
|
context.report({
|
|
36
|
-
node:
|
|
42
|
+
node: sourceCode.getFirstToken(
|
|
37
43
|
node.arguments[0],
|
|
38
44
|
token => token.value === "async",
|
|
39
45
|
),
|
|
@@ -20,7 +20,14 @@ const getPropertyName = require("./utils/ast-utils").getStaticPropertyName;
|
|
|
20
20
|
// Helpers
|
|
21
21
|
//------------------------------------------------------------------------------
|
|
22
22
|
|
|
23
|
-
const nonCallableGlobals = [
|
|
23
|
+
const nonCallableGlobals = [
|
|
24
|
+
"Atomics",
|
|
25
|
+
"JSON",
|
|
26
|
+
"Math",
|
|
27
|
+
"Reflect",
|
|
28
|
+
"Intl",
|
|
29
|
+
"Temporal",
|
|
30
|
+
];
|
|
24
31
|
|
|
25
32
|
/**
|
|
26
33
|
* Returns the name of the node to report
|
package/lib/rules/no-shadow.js
CHANGED
|
@@ -612,7 +612,7 @@ module.exports = {
|
|
|
612
612
|
|
|
613
613
|
/**
|
|
614
614
|
* Checks the current context for shadowed variables.
|
|
615
|
-
* @param {Scope} scope
|
|
615
|
+
* @param {Scope} scope The scope to check for shadowed variables.
|
|
616
616
|
* @returns {void}
|
|
617
617
|
*/
|
|
618
618
|
function checkForShadows(scope) {
|
|
@@ -128,6 +128,35 @@ function getEncloseFunctionDeclaration(reference) {
|
|
|
128
128
|
return null;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Checks whether a given modifier is in a loop.
|
|
133
|
+
*
|
|
134
|
+
* Besides checking for the condition being in the loop, this also checks
|
|
135
|
+
* whether the function that this modifier is belonging to is called
|
|
136
|
+
* in the loop.
|
|
137
|
+
* @param {LoopConditionInfo} condition The condition to check.
|
|
138
|
+
* @param {Reference} modifier The modifier to check.
|
|
139
|
+
* @returns {boolean} `true` if the modifier is in a loop.
|
|
140
|
+
*/
|
|
141
|
+
function hasModifierInLoop(condition, modifier) {
|
|
142
|
+
if (condition.isInLoop(modifier)) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const funcNode = getEncloseFunctionDeclaration(modifier);
|
|
147
|
+
|
|
148
|
+
if (!funcNode) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const funcVar = astUtils.getVariableByName(
|
|
153
|
+
modifier.from.upper,
|
|
154
|
+
funcNode.id.name,
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
return Boolean(funcVar && funcVar.references.some(condition.isInLoop));
|
|
158
|
+
}
|
|
159
|
+
|
|
131
160
|
/**
|
|
132
161
|
* Updates the "modified" flags of given loop conditions with given modifiers.
|
|
133
162
|
* @param {LoopConditionInfo[]} conditions The loop conditions to be updated.
|
|
@@ -140,26 +169,8 @@ function updateModifiedFlag(conditions, modifiers) {
|
|
|
140
169
|
|
|
141
170
|
for (let j = 0; !condition.modified && j < modifiers.length; ++j) {
|
|
142
171
|
const modifier = modifiers[j];
|
|
143
|
-
let funcNode, funcVar;
|
|
144
172
|
|
|
145
|
-
|
|
146
|
-
* Besides checking for the condition being in the loop, we want to
|
|
147
|
-
* check the function that this modifier is belonging to is called
|
|
148
|
-
* in the loop.
|
|
149
|
-
* FIXME: This should probably be extracted to a function.
|
|
150
|
-
*/
|
|
151
|
-
const inLoop =
|
|
152
|
-
condition.isInLoop(modifier) ||
|
|
153
|
-
Boolean(
|
|
154
|
-
(funcNode = getEncloseFunctionDeclaration(modifier)) &&
|
|
155
|
-
(funcVar = astUtils.getVariableByName(
|
|
156
|
-
modifier.from.upper,
|
|
157
|
-
funcNode.id.name,
|
|
158
|
-
)) &&
|
|
159
|
-
funcVar.references.some(condition.isInLoop),
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
condition.modified = inLoop;
|
|
173
|
+
condition.modified = hasModifierInLoop(condition, modifier);
|
|
163
174
|
}
|
|
164
175
|
}
|
|
165
176
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
module.exports = function ({ ruleIds, language }) {
|
|
4
|
+
return `
|
|
5
|
+
The following rules do not support the language "${language}":
|
|
6
|
+
${ruleIds.map(id => `\t- "${id}"`).join("\n")}
|
|
7
|
+
|
|
8
|
+
To fix this error, either:
|
|
9
|
+
- Remove the rule from your configuration, or set its severity to "off".
|
|
10
|
+
- Use the "files" option to apply the rule only to files of the supported language, for example:
|
|
11
|
+
{
|
|
12
|
+
files: ["**/*.js"],
|
|
13
|
+
rules: { "${ruleIds[0]}": "error" }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
See https://eslint.org/docs/latest/use/configure/rules for more information.
|
|
17
|
+
`.trimStart();
|
|
18
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint",
|
|
3
|
-
"version": "10.1
|
|
3
|
+
"version": "10.2.1",
|
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -119,10 +119,10 @@
|
|
|
119
119
|
"dependencies": {
|
|
120
120
|
"@eslint-community/eslint-utils": "^4.8.0",
|
|
121
121
|
"@eslint-community/regexpp": "^4.12.2",
|
|
122
|
-
"@eslint/config-array": "^0.23.
|
|
123
|
-
"@eslint/config-helpers": "^0.5.
|
|
124
|
-
"@eslint/core": "^1.
|
|
125
|
-
"@eslint/plugin-kit": "^0.
|
|
122
|
+
"@eslint/config-array": "^0.23.5",
|
|
123
|
+
"@eslint/config-helpers": "^0.5.5",
|
|
124
|
+
"@eslint/core": "^1.2.1",
|
|
125
|
+
"@eslint/plugin-kit": "^0.7.1",
|
|
126
126
|
"@humanfs/node": "^0.16.6",
|
|
127
127
|
"@humanwhocodes/module-importer": "^1.0.1",
|
|
128
128
|
"@humanwhocodes/retry": "^0.4.2",
|
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
"ejs": "^3.0.2",
|
|
169
169
|
"eslint": "file:.",
|
|
170
170
|
"eslint-config-eslint": "file:packages/eslint-config-eslint",
|
|
171
|
-
"eslint-plugin-eslint-plugin": "^
|
|
171
|
+
"eslint-plugin-eslint-plugin": "^7.3.2",
|
|
172
172
|
"eslint-plugin-expect-type": "^0.6.0",
|
|
173
173
|
"eslint-plugin-yml": "^1.14.0",
|
|
174
174
|
"eslint-release": "^3.3.0",
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
"lint-staged": "^11.0.0",
|
|
186
186
|
"markdown-it": "^12.2.0",
|
|
187
187
|
"markdown-it-container": "^3.0.0",
|
|
188
|
-
"markdownlint-cli2": "^0.
|
|
188
|
+
"markdownlint-cli2": "^0.22.0",
|
|
189
189
|
"marked": "^4.0.8",
|
|
190
190
|
"metascraper": "^5.25.7",
|
|
191
191
|
"metascraper-description": "^5.25.7",
|
|
@@ -196,7 +196,7 @@
|
|
|
196
196
|
"mocha": "^11.7.1",
|
|
197
197
|
"node-polyfill-webpack-plugin": "^1.0.3",
|
|
198
198
|
"npm-license": "^0.3.3",
|
|
199
|
-
"prettier": "3.8.
|
|
199
|
+
"prettier": "3.8.2",
|
|
200
200
|
"progress": "^2.0.3",
|
|
201
201
|
"proxyquire": "^2.0.1",
|
|
202
202
|
"recast": "^0.23.0",
|