eslint 10.1.0 → 10.2.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 +3 -1
- package/conf/globals.js +1 -0
- package/lib/config/config.js +129 -0
- package/lib/linter/linter.js +16 -0
- package/lib/rules/no-obj-calls.js +8 -1
- package/lib/rules/no-unmodified-loop-condition.js +30 -19
- package/messages/rule-unsupported-language.js +18 -0
- package/package.json +7 -7
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="#"><img src="https://images.opencollective.com/aeriusventilations-org/avatar.png" alt="aeriusventilation's Org" 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
|
/**
|
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":')
|
|
@@ -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
|
|
@@ -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.
|
|
3
|
+
"version": "10.2.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",
|
|
@@ -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.4",
|
|
123
|
+
"@eslint/config-helpers": "^0.5.4",
|
|
124
|
+
"@eslint/core": "^1.2.0",
|
|
125
|
+
"@eslint/plugin-kit": "^0.7.0",
|
|
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",
|