eslint 9.18.0 → 9.20.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 +25 -16
- package/lib/cli.js +8 -0
- package/lib/config/config-loader.js +34 -4
- package/lib/config/config.js +6 -1
- package/lib/config/flat-config-schema.js +16 -1
- package/lib/eslint/eslint.js +1 -1
- package/lib/linter/linter.js +98 -8
- package/lib/options.js +13 -0
- package/lib/rules/arrow-body-style.js +1 -1
- package/lib/rules/consistent-this.js +9 -0
- package/lib/shared/flags.js +43 -5
- package/lib/shared/option-utils.js +56 -0
- package/lib/types/index.d.ts +70 -60
- package/lib/types/rules/best-practices.d.ts +109 -95
- package/lib/types/rules/deprecated.d.ts +32 -15
- package/lib/types/rules/ecmascript-6.d.ts +39 -39
- package/lib/types/rules/node-commonjs.d.ts +23 -12
- package/lib/types/rules/possible-errors.d.ts +86 -54
- package/lib/types/rules/strict-mode.d.ts +1 -1
- package/lib/types/rules/stylistic-issues.d.ts +105 -99
- package/lib/types/rules/variables.d.ts +12 -12
- package/messages/config-serialize-function.js +28 -0
- package/messages/eslintrc-plugins.js +6 -2
- package/package.json +7 -5
package/README.md
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
[](https://www.npmjs.com/package/eslint)
|
2
2
|
[](https://www.npmjs.com/package/eslint)
|
3
3
|
[](https://github.com/eslint/eslint/actions)
|
4
|
-
|
5
|
-
<br />
|
4
|
+
<br>
|
6
5
|
[](https://opencollective.com/eslint)
|
7
6
|
[](https://opencollective.com/eslint)
|
8
|
-
[](https://twitter.com/intent/user?screen_name=geteslint)
|
9
7
|
|
10
8
|
# ESLint
|
11
9
|
|
@@ -17,7 +15,8 @@
|
|
17
15
|
[Code of Conduct](https://eslint.org/conduct) |
|
18
16
|
[Twitter](https://twitter.com/geteslint) |
|
19
17
|
[Discord](https://eslint.org/chat) |
|
20
|
-
[Mastodon](https://fosstodon.org/@eslint)
|
18
|
+
[Mastodon](https://fosstodon.org/@eslint) |
|
19
|
+
[Bluesky](https://bsky.app/profile/eslint.org)
|
21
20
|
|
22
21
|
ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code. In many ways, it is similar to JSLint and JSHint with a few exceptions:
|
23
22
|
|
@@ -36,7 +35,6 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/J
|
|
36
35
|
1. [Releases](#releases)
|
37
36
|
1. [Security Policy](#security-policy)
|
38
37
|
1. [Semantic Versioning Policy](#semantic-versioning-policy)
|
39
|
-
1. [Stylistic Rule Updates](#stylistic-rule-updates)
|
40
38
|
1. [License](#license)
|
41
39
|
1. [Team](#team)
|
42
40
|
1. [Sponsors](#sponsors)
|
@@ -192,18 +190,29 @@ ESLint follows [semantic versioning](https://semver.org). However, due to the na
|
|
192
190
|
|
193
191
|
According to our policy, any minor update may report more linting errors than the previous release (ex: from a bug fix). As such, we recommend using the tilde (`~`) in `package.json` e.g. `"eslint": "~3.1.0"` to guarantee the results of your builds.
|
194
192
|
|
195
|
-
##
|
193
|
+
## License
|
196
194
|
|
197
|
-
|
198
|
-
This means:
|
195
|
+
MIT License
|
199
196
|
|
200
|
-
|
201
|
-
* **New ECMAScript features**: We will also make sure stylistic rules are compatible with new ECMAScript features.
|
202
|
-
* **New options**: We will **not** add any new options to stylistic rules unless an option is the only way to fix a bug or support a newly-added ECMAScript feature.
|
197
|
+
Copyright OpenJS Foundation and other contributors, <www.openjsf.org>
|
203
198
|
|
204
|
-
|
199
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
200
|
+
of this software and associated documentation files (the "Software"), to deal
|
201
|
+
in the Software without restriction, including without limitation the rights
|
202
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
203
|
+
copies of the Software, and to permit persons to whom the Software is
|
204
|
+
furnished to do so, subject to the following conditions:
|
205
|
+
|
206
|
+
The above copyright notice and this permission notice shall be included in
|
207
|
+
all copies or substantial portions of the Software.
|
205
208
|
|
206
|
-
|
209
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
210
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
211
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
212
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
213
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
214
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
215
|
+
THE SOFTWARE.
|
207
216
|
|
208
217
|
## Team
|
209
218
|
|
@@ -298,9 +307,9 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
|
|
298
307
|
|
299
308
|
<h3>Platinum Sponsors</h3>
|
300
309
|
<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>
|
301
|
-
<p><a href="https://trunk.io/"><img src="https://images.opencollective.com/trunkio/fb92d60/avatar.png" alt="trunk.io" height="96"></a></p><h3>Silver Sponsors</h3>
|
302
|
-
<p><a href="https://www.serptriumph.com/"><img src="https://images.opencollective.com/serp-triumph5/fea3074/logo.png" alt="SERP Triumph" height="64"></a> <a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/fe76f99/logo.png" alt="JetBrains" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/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></p><h3>Bronze Sponsors</h3>
|
303
|
-
<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://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" 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></p>
|
310
|
+
<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></p><h3>Silver Sponsors</h3>
|
311
|
+
<p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/e6d15e1/logo.png" alt="Vite" height="64"></a> <a href="https://www.serptriumph.com/"><img src="https://images.opencollective.com/serp-triumph5/fea3074/logo.png" alt="SERP Triumph" height="64"></a> <a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/fe76f99/logo.png" alt="JetBrains" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/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></p><h3>Bronze Sponsors</h3>
|
312
|
+
<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://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" 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://nolebase.ayaka.io"><img src="https://avatars.githubusercontent.com/u/11081491" alt="Neko" 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></p>
|
304
313
|
<h3>Technology Sponsors</h3>
|
305
314
|
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.
|
306
315
|
<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/cli.js
CHANGED
@@ -128,6 +128,7 @@ async function translateOptions({
|
|
128
128
|
quiet,
|
129
129
|
reportUnusedDisableDirectives,
|
130
130
|
reportUnusedDisableDirectivesSeverity,
|
131
|
+
reportUnusedInlineConfigs,
|
131
132
|
resolvePluginsRelativeTo,
|
132
133
|
rule,
|
133
134
|
rulesdir,
|
@@ -180,6 +181,13 @@ async function translateOptions({
|
|
180
181
|
};
|
181
182
|
}
|
182
183
|
|
184
|
+
if (reportUnusedInlineConfigs !== void 0) {
|
185
|
+
overrideConfig[0].linterOptions = {
|
186
|
+
...overrideConfig[0].linterOptions,
|
187
|
+
reportUnusedInlineConfigs: normalizeSeverityToString(reportUnusedInlineConfigs)
|
188
|
+
};
|
189
|
+
}
|
190
|
+
|
183
191
|
if (plugin) {
|
184
192
|
overrideConfig[0].plugins = await loadPlugins(importer, plugin);
|
185
193
|
}
|
@@ -499,11 +499,41 @@ class ConfigLoader {
|
|
499
499
|
debug(`Loading config file ${configFilePath}`);
|
500
500
|
const fileConfig = await loadConfigFile(configFilePath);
|
501
501
|
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
502
|
+
/*
|
503
|
+
* It's possible that a config file could be empty or else
|
504
|
+
* have an empty object or array. In this case, we want to
|
505
|
+
* warn the user that they have an empty config.
|
506
|
+
*
|
507
|
+
* An empty CommonJS file exports an empty object while
|
508
|
+
* an empty ESM file exports undefined.
|
509
|
+
*/
|
510
|
+
|
511
|
+
let emptyConfig = typeof fileConfig === "undefined";
|
512
|
+
|
513
|
+
debug(`Config file ${configFilePath} is ${emptyConfig ? "empty" : "not empty"}`);
|
514
|
+
|
515
|
+
if (!emptyConfig) {
|
516
|
+
if (Array.isArray(fileConfig)) {
|
517
|
+
if (fileConfig.length === 0) {
|
518
|
+
debug(`Config file ${configFilePath} is an empty array`);
|
519
|
+
emptyConfig = true;
|
520
|
+
} else {
|
521
|
+
configs.push(...fileConfig);
|
522
|
+
}
|
523
|
+
} else {
|
524
|
+
if (typeof fileConfig === "object" && fileConfig !== null && Object.keys(fileConfig).length === 0) {
|
525
|
+
debug(`Config file ${configFilePath} is an empty object`);
|
526
|
+
emptyConfig = true;
|
527
|
+
} else {
|
528
|
+
configs.push(fileConfig);
|
529
|
+
}
|
530
|
+
}
|
506
531
|
}
|
532
|
+
|
533
|
+
if (emptyConfig) {
|
534
|
+
globalThis.process?.emitWarning?.(`Running ESLint with an empty config (from ${configFilePath}). Please double-check that this is what you want. If you want to run ESLint with an empty config, export [{}] to remove this warning.`, "ESLintEmptyConfigWarning");
|
535
|
+
}
|
536
|
+
|
507
537
|
}
|
508
538
|
|
509
539
|
// add in any configured defaults
|
package/lib/config/config.js
CHANGED
@@ -110,7 +110,12 @@ function languageOptionsToJSON(languageOptions, objectKey = "languageOptions") {
|
|
110
110
|
}
|
111
111
|
|
112
112
|
if (typeof value === "function") {
|
113
|
-
|
113
|
+
const error = new TypeError(`Cannot serialize key "${key}" in ${objectKey}: Function values are not supported.`);
|
114
|
+
|
115
|
+
error.messageTemplate = "config-serialize-function";
|
116
|
+
error.messageData = { key, objectKey };
|
117
|
+
|
118
|
+
throw error;
|
114
119
|
}
|
115
120
|
|
116
121
|
}
|
@@ -304,6 +304,20 @@ const disableDirectiveSeveritySchema = {
|
|
304
304
|
}
|
305
305
|
};
|
306
306
|
|
307
|
+
/** @type {ObjectPropertySchema} */
|
308
|
+
const unusedInlineConfigsSeveritySchema = {
|
309
|
+
merge(first, second) {
|
310
|
+
const value = second === void 0 ? first : second;
|
311
|
+
|
312
|
+
return normalizeSeverityToNumber(value);
|
313
|
+
},
|
314
|
+
validate(value) {
|
315
|
+
if (!ALLOWED_SEVERITIES.has(value)) {
|
316
|
+
throw new TypeError("Expected one of: \"error\", \"warn\", \"off\", 0, 1, or 2.");
|
317
|
+
}
|
318
|
+
}
|
319
|
+
};
|
320
|
+
|
307
321
|
/** @type {ObjectPropertySchema} */
|
308
322
|
const deepObjectAssignSchema = {
|
309
323
|
merge(first = {}, second = {}) {
|
@@ -555,7 +569,8 @@ const flatConfigSchema = {
|
|
555
569
|
linterOptions: {
|
556
570
|
schema: {
|
557
571
|
noInlineConfig: booleanSchema,
|
558
|
-
reportUnusedDisableDirectives: disableDirectiveSeveritySchema
|
572
|
+
reportUnusedDisableDirectives: disableDirectiveSeveritySchema,
|
573
|
+
reportUnusedInlineConfigs: unusedInlineConfigsSeveritySchema
|
559
574
|
}
|
560
575
|
},
|
561
576
|
language: languageSchema,
|
package/lib/eslint/eslint.js
CHANGED
@@ -470,7 +470,7 @@ class ESLint {
|
|
470
470
|
defaultConfigs
|
471
471
|
};
|
472
472
|
|
473
|
-
this.#configLoader =
|
473
|
+
this.#configLoader = linter.hasFlag("unstable_config_lookup_from_file")
|
474
474
|
? new ConfigLoader(configLoaderOptions)
|
475
475
|
: new LegacyConfigLoader(configLoaderOptions);
|
476
476
|
|
package/lib/linter/linter.js
CHANGED
@@ -40,10 +40,10 @@ const { FlatConfigArray } = require("../config/flat-config-array");
|
|
40
40
|
const { startTime, endTime } = require("../shared/stats");
|
41
41
|
const { RuleValidator } = require("../config/rule-validator");
|
42
42
|
const { assertIsRuleSeverity } = require("../config/flat-config-schema");
|
43
|
-
const { normalizeSeverityToString } = require("../shared/severity");
|
43
|
+
const { normalizeSeverityToString, normalizeSeverityToNumber } = require("../shared/severity");
|
44
44
|
const { deepMergeArrays } = require("../shared/deep-merge-arrays");
|
45
45
|
const jslang = require("../languages/js");
|
46
|
-
const { activeFlags, inactiveFlags } = require("../shared/flags");
|
46
|
+
const { activeFlags, inactiveFlags, getInactivityReasonMessage } = require("../shared/flags");
|
47
47
|
const debug = require("debug")("eslint:linter");
|
48
48
|
const MAX_AUTOFIX_PASSES = 10;
|
49
49
|
const DEFAULT_PARSER_NAME = "espree";
|
@@ -56,6 +56,7 @@ const { VFile } = require("./vfile");
|
|
56
56
|
const { ParserService } = require("../services/parser-service");
|
57
57
|
const { FileContext } = require("./file-context");
|
58
58
|
const { ProcessorService } = require("../services/processor-service");
|
59
|
+
const { containsDifferentProperty } = require("../shared/option-utils");
|
59
60
|
const STEP_KIND_VISIT = 1;
|
60
61
|
const STEP_KIND_CALL = 2;
|
61
62
|
|
@@ -76,6 +77,7 @@ const STEP_KIND_CALL = 2;
|
|
76
77
|
/** @typedef {import("@eslint/core").Language} Language */
|
77
78
|
/** @typedef {import("@eslint/core").RuleSeverity} RuleSeverity */
|
78
79
|
/** @typedef {import("@eslint/core").RuleConfig} RuleConfig */
|
80
|
+
/** @typedef {import("../types").Linter.StringSeverity} StringSeverity */
|
79
81
|
|
80
82
|
|
81
83
|
/* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
|
@@ -141,7 +143,8 @@ const STEP_KIND_CALL = 2;
|
|
141
143
|
/**
|
142
144
|
* @typedef {Object} InternalOptions
|
143
145
|
* @property {string | null} warnInlineConfig The config name what `noInlineConfig` setting came from. If `noInlineConfig` setting didn't exist, this is null. If this is a config name, then the linter warns directive comments.
|
144
|
-
* @property {
|
146
|
+
* @property {StringSeverity} reportUnusedDisableDirectives Severity to report unused disable directives, if not "off" (boolean values were normalized).
|
147
|
+
* @property {StringSeverity} reportUnusedInlineConfigs Severity to report unused inline configs, if not "off".
|
145
148
|
*/
|
146
149
|
|
147
150
|
//------------------------------------------------------------------------------
|
@@ -314,6 +317,62 @@ function createLintingProblem(options) {
|
|
314
317
|
};
|
315
318
|
}
|
316
319
|
|
320
|
+
/**
|
321
|
+
* Wraps the value in an Array if it isn't already one.
|
322
|
+
* @template T
|
323
|
+
* @param {T|T[]} value Value to be wrapped.
|
324
|
+
* @returns {Array} The value as an array.
|
325
|
+
*/
|
326
|
+
function asArray(value) {
|
327
|
+
return Array.isArray(value) ? value : [value];
|
328
|
+
}
|
329
|
+
|
330
|
+
/**
|
331
|
+
* Pushes a problem to inlineConfigProblems if ruleOptions are redundant.
|
332
|
+
* @param {ConfigData} config Provided config.
|
333
|
+
* @param {Object} loc A line/column location
|
334
|
+
* @param {Array} problems Problems that may be added to.
|
335
|
+
* @param {string} ruleId The rule ID.
|
336
|
+
* @param {Array} ruleOptions The rule options, merged with the config's.
|
337
|
+
* @param {Array} ruleOptionsInline The rule options from the comment.
|
338
|
+
* @param {"error"|"warn"} severity The severity to report.
|
339
|
+
* @returns {void}
|
340
|
+
*/
|
341
|
+
function addProblemIfSameSeverityAndOptions(config, loc, problems, ruleId, ruleOptions, ruleOptionsInline, severity) {
|
342
|
+
const existingConfigRaw = config.rules?.[ruleId];
|
343
|
+
const existingConfig = existingConfigRaw ? asArray(existingConfigRaw) : ["off"];
|
344
|
+
const existingSeverity = normalizeSeverityToString(existingConfig[0]);
|
345
|
+
const inlineSeverity = normalizeSeverityToString(ruleOptions[0]);
|
346
|
+
const sameSeverity = existingSeverity === inlineSeverity;
|
347
|
+
|
348
|
+
if (!sameSeverity) {
|
349
|
+
return;
|
350
|
+
}
|
351
|
+
|
352
|
+
const alreadyConfigured = existingConfigRaw
|
353
|
+
? `is already configured to '${existingSeverity}'`
|
354
|
+
: "is not enabled so can't be turned off";
|
355
|
+
let message;
|
356
|
+
|
357
|
+
if ((existingConfig.length === 1 && ruleOptions.length === 1) || existingSeverity === "off") {
|
358
|
+
message = `Unused inline config ('${ruleId}' ${alreadyConfigured}).`;
|
359
|
+
} else if (!containsDifferentProperty(ruleOptions.slice(1), existingConfig.slice(1))) {
|
360
|
+
message = ruleOptionsInline.length === 1
|
361
|
+
? `Unused inline config ('${ruleId}' ${alreadyConfigured}).`
|
362
|
+
: `Unused inline config ('${ruleId}' ${alreadyConfigured} with the same options).`;
|
363
|
+
}
|
364
|
+
|
365
|
+
if (message) {
|
366
|
+
problems.push(createLintingProblem({
|
367
|
+
ruleId: null,
|
368
|
+
message,
|
369
|
+
loc,
|
370
|
+
language: config.language,
|
371
|
+
severity: normalizeSeverityToNumber(severity)
|
372
|
+
}));
|
373
|
+
}
|
374
|
+
}
|
375
|
+
|
317
376
|
/**
|
318
377
|
* Creates a collection of disable directives from a comment
|
319
378
|
* @param {Object} options to create disable directives
|
@@ -517,7 +576,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
517
576
|
return;
|
518
577
|
}
|
519
578
|
|
520
|
-
let ruleOptions =
|
579
|
+
let ruleOptions = asArray(ruleValue);
|
521
580
|
|
522
581
|
/*
|
523
582
|
* If the rule was already configured, inline rule configuration that
|
@@ -555,7 +614,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
555
614
|
*/
|
556
615
|
ruleOptions = [
|
557
616
|
ruleOptions[0], // severity from the inline config
|
558
|
-
...
|
617
|
+
...asArray(config.rules[name]).slice(1) // options from the provided config
|
559
618
|
];
|
560
619
|
}
|
561
620
|
|
@@ -774,6 +833,8 @@ function normalizeVerifyOptions(providedOptions, config) {
|
|
774
833
|
}
|
775
834
|
}
|
776
835
|
|
836
|
+
const reportUnusedInlineConfigs = linterOptions.reportUnusedInlineConfigs === void 0 ? "off" : normalizeSeverityToString(linterOptions.reportUnusedInlineConfigs);
|
837
|
+
|
777
838
|
let ruleFilter = providedOptions.ruleFilter;
|
778
839
|
|
779
840
|
if (typeof ruleFilter !== "function") {
|
@@ -787,6 +848,7 @@ function normalizeVerifyOptions(providedOptions, config) {
|
|
787
848
|
? `your config${configNameOfNoInlineConfig}`
|
788
849
|
: null,
|
789
850
|
reportUnusedDisableDirectives,
|
851
|
+
reportUnusedInlineConfigs,
|
790
852
|
disableFixes: Boolean(providedOptions.disableFixes),
|
791
853
|
stats: providedOptions.stats,
|
792
854
|
ruleFilter
|
@@ -1264,19 +1326,40 @@ class Linter {
|
|
1264
1326
|
*/
|
1265
1327
|
constructor({ cwd, configType = "flat", flags = [] } = {}) {
|
1266
1328
|
|
1329
|
+
const processedFlags = [];
|
1330
|
+
|
1267
1331
|
flags.forEach(flag => {
|
1268
1332
|
if (inactiveFlags.has(flag)) {
|
1269
|
-
|
1333
|
+
const inactiveFlagData = inactiveFlags.get(flag);
|
1334
|
+
const inactivityReason = getInactivityReasonMessage(inactiveFlagData);
|
1335
|
+
|
1336
|
+
if (typeof inactiveFlagData.replacedBy === "undefined") {
|
1337
|
+
throw new Error(`The flag '${flag}' is inactive: ${inactivityReason}`);
|
1338
|
+
}
|
1339
|
+
|
1340
|
+
// if there's a replacement, enable it instead of original
|
1341
|
+
if (typeof inactiveFlagData.replacedBy === "string") {
|
1342
|
+
processedFlags.push(inactiveFlagData.replacedBy);
|
1343
|
+
}
|
1344
|
+
|
1345
|
+
globalThis.process?.emitWarning?.(
|
1346
|
+
`The flag '${flag}' is inactive: ${inactivityReason}`,
|
1347
|
+
`ESLintInactiveFlag_${flag}`
|
1348
|
+
);
|
1349
|
+
|
1350
|
+
return;
|
1270
1351
|
}
|
1271
1352
|
|
1272
1353
|
if (!activeFlags.has(flag)) {
|
1273
1354
|
throw new Error(`Unknown flag '${flag}'.`);
|
1274
1355
|
}
|
1356
|
+
|
1357
|
+
processedFlags.push(flag);
|
1275
1358
|
});
|
1276
1359
|
|
1277
1360
|
internalSlotsMap.set(this, {
|
1278
1361
|
cwd: normalizeCwd(cwd),
|
1279
|
-
flags,
|
1362
|
+
flags: processedFlags,
|
1280
1363
|
lastConfigArray: null,
|
1281
1364
|
lastSourceCode: null,
|
1282
1365
|
lastSuppressedMessages: [],
|
@@ -1787,7 +1870,8 @@ class Linter {
|
|
1787
1870
|
|
1788
1871
|
try {
|
1789
1872
|
|
1790
|
-
|
1873
|
+
const ruleOptionsInline = asArray(ruleValue);
|
1874
|
+
let ruleOptions = ruleOptionsInline;
|
1791
1875
|
|
1792
1876
|
assertIsRuleSeverity(ruleId, ruleOptions[0]);
|
1793
1877
|
|
@@ -1850,6 +1934,12 @@ class Linter {
|
|
1850
1934
|
}
|
1851
1935
|
}
|
1852
1936
|
|
1937
|
+
if (options.reportUnusedInlineConfigs !== "off") {
|
1938
|
+
addProblemIfSameSeverityAndOptions(
|
1939
|
+
config, loc, inlineConfigProblems, ruleId, ruleOptions, ruleOptionsInline, options.reportUnusedInlineConfigs
|
1940
|
+
);
|
1941
|
+
}
|
1942
|
+
|
1853
1943
|
if (shouldValidateOptions) {
|
1854
1944
|
ruleValidator.validate({
|
1855
1945
|
plugins: config.plugins,
|
package/lib/options.js
CHANGED
@@ -187,6 +187,18 @@ module.exports = function(usingFlatConfig) {
|
|
187
187
|
};
|
188
188
|
}
|
189
189
|
|
190
|
+
let reportUnusedInlineConfigsFlag;
|
191
|
+
|
192
|
+
if (usingFlatConfig) {
|
193
|
+
reportUnusedInlineConfigsFlag = {
|
194
|
+
option: "report-unused-inline-configs",
|
195
|
+
type: "String",
|
196
|
+
default: void 0,
|
197
|
+
description: "Adds reported errors for unused eslint inline config comments",
|
198
|
+
enum: ["off", "warn", "error", "0", "1", "2"]
|
199
|
+
};
|
200
|
+
}
|
201
|
+
|
190
202
|
return optionator({
|
191
203
|
prepend: "eslint [options] file.js [file.js] [dir]",
|
192
204
|
defaults: {
|
@@ -350,6 +362,7 @@ module.exports = function(usingFlatConfig) {
|
|
350
362
|
description: "Chooses severity level for reporting unused eslint-disable and eslint-enable directives",
|
351
363
|
enum: ["off", "warn", "error", "0", "1", "2"]
|
352
364
|
},
|
365
|
+
reportUnusedInlineConfigsFlag,
|
353
366
|
{
|
354
367
|
heading: "Caching"
|
355
368
|
},
|
@@ -143,7 +143,7 @@ module.exports = {
|
|
143
143
|
|
144
144
|
if (blockBody.length === 0) {
|
145
145
|
messageId = "unexpectedEmptyBlock";
|
146
|
-
} else if (blockBody.length > 1) {
|
146
|
+
} else if (blockBody.length > 1 || blockBody[0].type !== "ReturnStatement") {
|
147
147
|
messageId = "unexpectedOtherBlock";
|
148
148
|
} else if (blockBody[0].argument === null) {
|
149
149
|
messageId = "unexpectedSingleBlock";
|
@@ -119,8 +119,17 @@ module.exports = {
|
|
119
119
|
function ensureWasAssigned(node) {
|
120
120
|
const scope = sourceCode.getScope(node);
|
121
121
|
|
122
|
+
// if this is program scope we also need to check module scope
|
123
|
+
const extraScope = node.type === "Program" && node.sourceType === "module"
|
124
|
+
? scope.childScopes[0]
|
125
|
+
: null;
|
126
|
+
|
122
127
|
aliases.forEach(alias => {
|
123
128
|
checkWasAssigned(alias, scope);
|
129
|
+
|
130
|
+
if (extraScope) {
|
131
|
+
checkWasAssigned(alias, extraScope);
|
132
|
+
}
|
124
133
|
});
|
125
134
|
}
|
126
135
|
|
package/lib/shared/flags.js
CHANGED
@@ -4,6 +4,23 @@
|
|
4
4
|
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Typedefs
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
/**
|
12
|
+
* @typedef {Object} InactiveFlagData
|
13
|
+
* @property {string} description Flag description
|
14
|
+
* @property {string | null} [replacedBy] Can be either:
|
15
|
+
* - An active flag (string) that enables the same feature.
|
16
|
+
* - `null` if the feature is now enabled by default.
|
17
|
+
* - Omitted if the feature has been abandoned.
|
18
|
+
*/
|
19
|
+
|
20
|
+
//-----------------------------------------------------------------------------
|
21
|
+
// Exports
|
22
|
+
//-----------------------------------------------------------------------------
|
23
|
+
|
7
24
|
/**
|
8
25
|
* The set of flags that change ESLint behavior with a description.
|
9
26
|
* @type {Map<string, string>}
|
@@ -14,15 +31,36 @@ const activeFlags = new Map([
|
|
14
31
|
]);
|
15
32
|
|
16
33
|
/**
|
17
|
-
* The set of flags that used to be active
|
18
|
-
* @type {Map<string,
|
34
|
+
* The set of flags that used to be active.
|
35
|
+
* @type {Map<string, InactiveFlagData>}
|
19
36
|
*/
|
20
37
|
const inactiveFlags = new Map([
|
21
|
-
["
|
22
|
-
["
|
38
|
+
["test_only_replaced", { description: "Used only for testing flags that have been replaced by other flags.", replacedBy: "test_only" }],
|
39
|
+
["test_only_enabled_by_default", { description: "Used only for testing flags whose features have been enabled by default.", replacedBy: null }],
|
40
|
+
["test_only_abandoned", { description: "Used only for testing flags whose features have been abandoned." }],
|
41
|
+
["unstable_ts_config", { description: "Enable TypeScript configuration files.", replacedBy: null }]
|
23
42
|
]);
|
24
43
|
|
44
|
+
/**
|
45
|
+
* Creates a message that describes the reason the flag is inactive.
|
46
|
+
* @param {InactiveFlagData} inactiveFlagData Data for the inactive flag.
|
47
|
+
* @returns {string} Message describing the reason the flag is inactive.
|
48
|
+
*/
|
49
|
+
function getInactivityReasonMessage({ replacedBy }) {
|
50
|
+
if (typeof replacedBy === "undefined") {
|
51
|
+
return "This feature has been abandoned.";
|
52
|
+
}
|
53
|
+
|
54
|
+
if (typeof replacedBy === "string") {
|
55
|
+
return `This flag has been renamed '${replacedBy}' to reflect its stabilization. Please use '${replacedBy}' instead.`;
|
56
|
+
}
|
57
|
+
|
58
|
+
// null
|
59
|
+
return "This feature is now enabled by default.";
|
60
|
+
}
|
61
|
+
|
25
62
|
module.exports = {
|
26
63
|
activeFlags,
|
27
|
-
inactiveFlags
|
64
|
+
inactiveFlags,
|
65
|
+
getInactivityReasonMessage
|
28
66
|
};
|
@@ -0,0 +1,56 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Utilities to operate on option objects.
|
3
|
+
* @author Josh Goldberg
|
4
|
+
*/
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Determines whether any of input's properties are different
|
10
|
+
* from values that already exist in original.
|
11
|
+
* @template T
|
12
|
+
* @param {Partial<T>} input New value.
|
13
|
+
* @param {T} original Original value.
|
14
|
+
* @returns {boolean} Whether input includes an explicit difference.
|
15
|
+
*/
|
16
|
+
function containsDifferentProperty(input, original) {
|
17
|
+
if (input === original) {
|
18
|
+
return false;
|
19
|
+
}
|
20
|
+
|
21
|
+
if (
|
22
|
+
typeof input !== typeof original ||
|
23
|
+
Array.isArray(input) !== Array.isArray(original)
|
24
|
+
) {
|
25
|
+
return true;
|
26
|
+
}
|
27
|
+
|
28
|
+
if (Array.isArray(input)) {
|
29
|
+
return (
|
30
|
+
input.length !== original.length ||
|
31
|
+
input.some((value, i) =>
|
32
|
+
containsDifferentProperty(value, original[i]))
|
33
|
+
);
|
34
|
+
}
|
35
|
+
|
36
|
+
if (typeof input === "object") {
|
37
|
+
if (input === null || original === null) {
|
38
|
+
return true;
|
39
|
+
}
|
40
|
+
|
41
|
+
const inputKeys = Object.keys(input);
|
42
|
+
const originalKeys = Object.keys(original);
|
43
|
+
|
44
|
+
return inputKeys.length !== originalKeys.length || inputKeys.some(
|
45
|
+
inputKey =>
|
46
|
+
!Object.hasOwn(original, inputKey) ||
|
47
|
+
containsDifferentProperty(input[inputKey], original[inputKey])
|
48
|
+
);
|
49
|
+
}
|
50
|
+
|
51
|
+
return true;
|
52
|
+
}
|
53
|
+
|
54
|
+
module.exports = {
|
55
|
+
containsDifferentProperty
|
56
|
+
};
|