eslint 9.4.0 → 9.6.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 -3
- package/conf/ecma-version.js +1 -1
- package/conf/globals.js +6 -1
- package/lib/api.js +1 -1
- package/lib/cli.js +23 -2
- package/lib/config/default-config.js +5 -0
- package/lib/config/flat-config-array.js +71 -8
- package/lib/config/flat-config-schema.js +46 -62
- package/lib/eslint/eslint-helpers.js +32 -17
- package/lib/eslint/eslint.js +30 -12
- package/lib/eslint/legacy-eslint.js +14 -0
- package/lib/languages/js/index.js +247 -0
- package/lib/{source-code → languages/js/source-code}/source-code.js +46 -22
- package/lib/languages/js/validate-language-options.js +181 -0
- package/lib/linter/apply-disable-directives.js +8 -3
- package/lib/linter/config-comment-parser.js +3 -16
- package/lib/linter/linter.js +291 -249
- package/lib/linter/report-translator.js +14 -7
- package/lib/linter/vfile.js +104 -0
- package/lib/options.js +13 -1
- package/lib/rule-tester/rule-tester.js +5 -2
- package/lib/rules/no-sparse-arrays.js +26 -3
- package/lib/rules/no-unused-vars.js +33 -31
- package/lib/shared/flags.js +26 -0
- package/lib/shared/logging.js +10 -1
- package/lib/shared/types.js +1 -1
- package/messages/all-matched-files-ignored.js +21 -0
- package/package.json +13 -18
- /package/lib/{source-code → languages/js/source-code}/index.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/backward-token-comment-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/backward-token-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/cursors.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/decorative-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/filter-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/forward-token-comment-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/forward-token-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/index.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/limit-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/padded-token-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/skip-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/utils.js +0 -0
package/README.md
CHANGED
@@ -289,13 +289,13 @@ Percy Ma
|
|
289
289
|
|
290
290
|
## Sponsors
|
291
291
|
|
292
|
-
The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://
|
292
|
+
The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://eslint.org/donate) to get your logo on our README and website.
|
293
293
|
|
294
294
|
<!-- NOTE: This section is autogenerated. Do not manually edit.-->
|
295
295
|
<!--sponsorsstart-->
|
296
296
|
<h3>Platinum Sponsors</h3>
|
297
|
-
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
|
298
|
-
<p><a href="
|
297
|
+
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="undefined"></a></p><h3>Gold Sponsors</h3>
|
298
|
+
<p><a href="#"><img src="https://images.opencollective.com/guest-bf377e88/avatar.png" alt="Eli Schleifer" height="96"></a> <a href="https://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a></p><h3>Silver Sponsors</h3>
|
299
299
|
<p><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?v=4" alt="American Express" height="64"></a> <a href="https://www.workleap.com"><img src="https://avatars.githubusercontent.com/u/53535748?u=d1e55d7661d724bf2281c1bfd33cb8f99fe2465f&v=4" alt="Workleap" height="64"></a></p><h3>Bronze Sponsors</h3>
|
300
300
|
<p><a href="https://www.notion.so"><img src="https://images.opencollective.com/notion/bf3b117/logo.png" alt="notion" 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.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104?v=4" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://usenextbase.com"><img src="https://avatars.githubusercontent.com/u/145838380?v=4" alt="Nextbase Starter Kit" height="32"></a></p>
|
301
301
|
<!--sponsorsend-->
|
package/conf/ecma-version.js
CHANGED
package/conf/globals.js
CHANGED
@@ -133,6 +133,10 @@ const es2024 = {
|
|
133
133
|
...es2023
|
134
134
|
};
|
135
135
|
|
136
|
+
const es2025 = {
|
137
|
+
...es2024
|
138
|
+
};
|
139
|
+
|
136
140
|
|
137
141
|
//-----------------------------------------------------------------------------
|
138
142
|
// Exports
|
@@ -151,5 +155,6 @@ module.exports = {
|
|
151
155
|
es2021,
|
152
156
|
es2022,
|
153
157
|
es2023,
|
154
|
-
es2024
|
158
|
+
es2024,
|
159
|
+
es2025
|
155
160
|
};
|
package/lib/api.js
CHANGED
@@ -13,7 +13,7 @@ const { ESLint, shouldUseFlatConfig } = require("./eslint/eslint");
|
|
13
13
|
const { LegacyESLint } = require("./eslint/legacy-eslint");
|
14
14
|
const { Linter } = require("./linter");
|
15
15
|
const { RuleTester } = require("./rule-tester");
|
16
|
-
const { SourceCode } = require("./source-code");
|
16
|
+
const { SourceCode } = require("./languages/js/source-code");
|
17
17
|
|
18
18
|
//-----------------------------------------------------------------------------
|
19
19
|
// Functions
|
package/lib/cli.js
CHANGED
@@ -26,7 +26,7 @@ const fs = require("node:fs"),
|
|
26
26
|
{ normalizeSeverityToString } = require("./shared/severity");
|
27
27
|
const { Legacy: { naming } } = require("@eslint/eslintrc");
|
28
28
|
const { ModuleImporter } = require("@humanwhocodes/module-importer");
|
29
|
-
|
29
|
+
const { inactiveFlags, activeFlags } = require("./shared/flags");
|
30
30
|
const debug = require("debug")("eslint:cli");
|
31
31
|
|
32
32
|
//------------------------------------------------------------------------------
|
@@ -117,6 +117,7 @@ async function translateOptions({
|
|
117
117
|
fix,
|
118
118
|
fixDryRun,
|
119
119
|
fixType,
|
120
|
+
flag,
|
120
121
|
global,
|
121
122
|
ignore,
|
122
123
|
ignorePath,
|
@@ -225,6 +226,7 @@ async function translateOptions({
|
|
225
226
|
options.ignorePatterns = ignorePattern;
|
226
227
|
options.stats = stats;
|
227
228
|
options.warnIgnored = warnIgnored;
|
229
|
+
options.flags = flag;
|
228
230
|
|
229
231
|
/*
|
230
232
|
* For performance reasons rules not marked as 'error' are filtered out in quiet mode. As maxWarnings
|
@@ -485,8 +487,27 @@ const cli = {
|
|
485
487
|
}
|
486
488
|
|
487
489
|
const ActiveESLint = usingFlatConfig ? ESLint : LegacyESLint;
|
490
|
+
const eslintOptions = await translateOptions(options, usingFlatConfig ? "flat" : "eslintrc");
|
491
|
+
|
492
|
+
if (eslintOptions.flags) {
|
493
|
+
debug("Checking for inactive flags");
|
494
|
+
|
495
|
+
for (const flag of eslintOptions.flags) {
|
496
|
+
if (inactiveFlags.has(flag)) {
|
497
|
+
log.warn(`InactiveFlag: The '${flag}' flag is no longer active: ${inactiveFlags.get(flag)}`);
|
498
|
+
continue;
|
499
|
+
}
|
500
|
+
|
501
|
+
if (activeFlags.has(flag)) {
|
502
|
+
continue;
|
503
|
+
}
|
504
|
+
|
505
|
+
log.error(`InvalidFlag: The '${flag}' flag is invalid.`);
|
506
|
+
return 2;
|
507
|
+
}
|
508
|
+
}
|
488
509
|
|
489
|
-
const engine = new ActiveESLint(
|
510
|
+
const engine = new ActiveESLint(eslintOptions);
|
490
511
|
let results;
|
491
512
|
|
492
513
|
if (useStdin) {
|
@@ -20,6 +20,10 @@ exports.defaultConfig = [
|
|
20
20
|
plugins: {
|
21
21
|
"@": {
|
22
22
|
|
23
|
+
languages: {
|
24
|
+
js: require("../languages/js")
|
25
|
+
},
|
26
|
+
|
23
27
|
/*
|
24
28
|
* Because we try to delay loading rules until absolutely
|
25
29
|
* necessary, a proxy allows us to hook into the lazy-loading
|
@@ -37,6 +41,7 @@ exports.defaultConfig = [
|
|
37
41
|
})
|
38
42
|
}
|
39
43
|
},
|
44
|
+
language: "@/js",
|
40
45
|
languageOptions: {
|
41
46
|
sourceType: "module",
|
42
47
|
ecmaVersion: "latest",
|
@@ -10,7 +10,7 @@
|
|
10
10
|
//-----------------------------------------------------------------------------
|
11
11
|
|
12
12
|
const { ConfigArray, ConfigArraySymbol } = require("@eslint/config-array");
|
13
|
-
const { flatConfigSchema } = require("./flat-config-schema");
|
13
|
+
const { flatConfigSchema, hasMethod } = require("./flat-config-schema");
|
14
14
|
const { RuleValidator } = require("./rule-validator");
|
15
15
|
const { defaultConfig } = require("./default-config");
|
16
16
|
|
@@ -123,6 +123,43 @@ function wrapConfigErrorWithDetails(error, originalLength, baseLength) {
|
|
123
123
|
);
|
124
124
|
}
|
125
125
|
|
126
|
+
/**
|
127
|
+
* Converts a languageOptions object to a JSON representation.
|
128
|
+
* @param {Record<string, any>} languageOptions The options to create a JSON
|
129
|
+
* representation of.
|
130
|
+
* @param {string} objectKey The key of the object being converted.
|
131
|
+
* @returns {Record<string, any>} The JSON representation of the languageOptions.
|
132
|
+
* @throws {TypeError} If a function is found in the languageOptions.
|
133
|
+
*/
|
134
|
+
function languageOptionsToJSON(languageOptions, objectKey = "languageOptions") {
|
135
|
+
|
136
|
+
const result = {};
|
137
|
+
|
138
|
+
for (const [key, value] of Object.entries(languageOptions)) {
|
139
|
+
if (value) {
|
140
|
+
if (typeof value === "object") {
|
141
|
+
const name = getObjectId(value);
|
142
|
+
|
143
|
+
if (name && hasMethod(value)) {
|
144
|
+
result[key] = name;
|
145
|
+
} else {
|
146
|
+
result[key] = languageOptionsToJSON(value, key);
|
147
|
+
}
|
148
|
+
continue;
|
149
|
+
}
|
150
|
+
|
151
|
+
if (typeof value === "function") {
|
152
|
+
throw new TypeError(`Cannot serialize key "${key}" in ${objectKey}: Function values are not supported.`);
|
153
|
+
}
|
154
|
+
|
155
|
+
}
|
156
|
+
|
157
|
+
result[key] = value;
|
158
|
+
}
|
159
|
+
|
160
|
+
return result;
|
161
|
+
}
|
162
|
+
|
126
163
|
const originalBaseConfig = Symbol("originalBaseConfig");
|
127
164
|
const originalLength = Symbol("originalLength");
|
128
165
|
const baseLength = Symbol("baseLength");
|
@@ -269,10 +306,11 @@ class FlatConfigArray extends ConfigArray {
|
|
269
306
|
*/
|
270
307
|
[ConfigArraySymbol.finalizeConfig](config) {
|
271
308
|
|
272
|
-
const { plugins, languageOptions, processor } = config;
|
273
|
-
let parserName, processorName;
|
309
|
+
const { plugins, language, languageOptions, processor } = config;
|
310
|
+
let parserName, processorName, languageName;
|
274
311
|
let invalidParser = false,
|
275
|
-
invalidProcessor = false
|
312
|
+
invalidProcessor = false,
|
313
|
+
invalidLanguage = false;
|
276
314
|
|
277
315
|
// Check parser value
|
278
316
|
if (languageOptions && languageOptions.parser) {
|
@@ -290,6 +328,29 @@ class FlatConfigArray extends ConfigArray {
|
|
290
328
|
}
|
291
329
|
}
|
292
330
|
|
331
|
+
// Check language value
|
332
|
+
if (language) {
|
333
|
+
if (typeof language === "string") {
|
334
|
+
const { pluginName, objectName: localLanguageName } = splitPluginIdentifier(language);
|
335
|
+
|
336
|
+
languageName = language;
|
337
|
+
|
338
|
+
if (!plugins || !plugins[pluginName] || !plugins[pluginName].languages || !plugins[pluginName].languages[localLanguageName]) {
|
339
|
+
throw new TypeError(`Key "language": Could not find "${localLanguageName}" in plugin "${pluginName}".`);
|
340
|
+
}
|
341
|
+
|
342
|
+
config.language = plugins[pluginName].languages[localLanguageName];
|
343
|
+
} else {
|
344
|
+
invalidLanguage = true;
|
345
|
+
}
|
346
|
+
|
347
|
+
try {
|
348
|
+
config.language.validateLanguageOptions(config.languageOptions);
|
349
|
+
} catch (error) {
|
350
|
+
throw new TypeError(`Key "languageOptions": ${error.message}`, { cause: error });
|
351
|
+
}
|
352
|
+
}
|
353
|
+
|
293
354
|
// Check processor value
|
294
355
|
if (processor) {
|
295
356
|
if (typeof processor === "string") {
|
@@ -329,6 +390,10 @@ class FlatConfigArray extends ConfigArray {
|
|
329
390
|
throw new Error("Could not serialize processor object (missing 'meta' object).");
|
330
391
|
}
|
331
392
|
|
393
|
+
if (invalidLanguage) {
|
394
|
+
throw new Error("Caching is not supported when language is an object.");
|
395
|
+
}
|
396
|
+
|
332
397
|
return {
|
333
398
|
...this,
|
334
399
|
plugins: Object.entries(plugins).map(([namespace, plugin]) => {
|
@@ -341,10 +406,8 @@ class FlatConfigArray extends ConfigArray {
|
|
341
406
|
|
342
407
|
return `${namespace}:${pluginId}`;
|
343
408
|
}),
|
344
|
-
|
345
|
-
|
346
|
-
parser: parserName
|
347
|
-
},
|
409
|
+
language: languageName,
|
410
|
+
languageOptions: languageOptionsToJSON(languageOptions),
|
348
411
|
processor: processorName
|
349
412
|
};
|
350
413
|
}
|
@@ -33,12 +33,6 @@ const ruleSeverities = new Map([
|
|
33
33
|
[2, 2], ["error", 2]
|
34
34
|
]);
|
35
35
|
|
36
|
-
const globalVariablesValues = new Set([
|
37
|
-
true, "true", "writable", "writeable",
|
38
|
-
false, "false", "readonly", "readable", null,
|
39
|
-
"off"
|
40
|
-
]);
|
41
|
-
|
42
36
|
/**
|
43
37
|
* Check if a value is a non-null object.
|
44
38
|
* @param {any} value The value to check.
|
@@ -143,6 +137,23 @@ function normalizeRuleOptions(ruleOptions) {
|
|
143
137
|
return structuredClone(finalOptions);
|
144
138
|
}
|
145
139
|
|
140
|
+
/**
|
141
|
+
* Determines if an object has any methods.
|
142
|
+
* @param {Object} object The object to check.
|
143
|
+
* @returns {boolean} `true` if the object has any methods.
|
144
|
+
*/
|
145
|
+
function hasMethod(object) {
|
146
|
+
|
147
|
+
for (const key of Object.keys(object)) {
|
148
|
+
|
149
|
+
if (typeof object[key] === "function") {
|
150
|
+
return true;
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
return false;
|
155
|
+
}
|
156
|
+
|
146
157
|
//-----------------------------------------------------------------------------
|
147
158
|
// Assertions
|
148
159
|
//-----------------------------------------------------------------------------
|
@@ -301,47 +312,48 @@ const deepObjectAssignSchema = {
|
|
301
312
|
validate: "object"
|
302
313
|
};
|
303
314
|
|
315
|
+
|
304
316
|
//-----------------------------------------------------------------------------
|
305
317
|
// High-Level Schemas
|
306
318
|
//-----------------------------------------------------------------------------
|
307
319
|
|
308
320
|
/** @type {ObjectPropertySchema} */
|
309
|
-
const
|
310
|
-
merge
|
311
|
-
validate(value) {
|
321
|
+
const languageOptionsSchema = {
|
322
|
+
merge(first = {}, second = {}) {
|
312
323
|
|
313
|
-
|
324
|
+
const result = deepMerge(first, second);
|
314
325
|
|
315
|
-
for (const key of Object.
|
326
|
+
for (const [key, value] of Object.entries(result)) {
|
316
327
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
328
|
+
/*
|
329
|
+
* Special case: Because the `parser` property is an object, it should
|
330
|
+
* not be deep merged. Instead, it should be replaced if it exists in
|
331
|
+
* the second object. To make this more generic, we just check for
|
332
|
+
* objects with methods and replace them if they exist in the second
|
333
|
+
* object.
|
334
|
+
*/
|
335
|
+
if (isNonArrayObject(value)) {
|
336
|
+
if (hasMethod(value)) {
|
337
|
+
result[key] = second[key] ?? first[key];
|
338
|
+
continue;
|
339
|
+
}
|
321
340
|
|
322
|
-
|
323
|
-
|
341
|
+
// for other objects, make sure we aren't reusing the same object
|
342
|
+
result[key] = { ...result[key] };
|
343
|
+
continue;
|
324
344
|
}
|
325
345
|
|
326
|
-
if (!globalVariablesValues.has(value[key])) {
|
327
|
-
throw new TypeError(`Key "${key}": Expected "readonly", "writable", or "off".`);
|
328
|
-
}
|
329
346
|
}
|
330
|
-
|
347
|
+
|
348
|
+
return result;
|
349
|
+
},
|
350
|
+
validate: "object"
|
331
351
|
};
|
332
352
|
|
333
353
|
/** @type {ObjectPropertySchema} */
|
334
|
-
const
|
354
|
+
const languageSchema = {
|
335
355
|
merge: "replace",
|
336
|
-
validate
|
337
|
-
|
338
|
-
if (!value || typeof value !== "object" ||
|
339
|
-
(typeof value.parse !== "function" && typeof value.parseForESLint !== "function")
|
340
|
-
) {
|
341
|
-
throw new TypeError("Expected object with parse() or parseForESLint() method.");
|
342
|
-
}
|
343
|
-
|
344
|
-
}
|
356
|
+
validate: assertIsPluginMemberName
|
345
357
|
};
|
346
358
|
|
347
359
|
/** @type {ObjectPropertySchema} */
|
@@ -501,28 +513,6 @@ const rulesSchema = {
|
|
501
513
|
}
|
502
514
|
};
|
503
515
|
|
504
|
-
/** @type {ObjectPropertySchema} */
|
505
|
-
const ecmaVersionSchema = {
|
506
|
-
merge: "replace",
|
507
|
-
validate(value) {
|
508
|
-
if (typeof value === "number" || value === "latest") {
|
509
|
-
return;
|
510
|
-
}
|
511
|
-
|
512
|
-
throw new TypeError("Expected a number or \"latest\".");
|
513
|
-
}
|
514
|
-
};
|
515
|
-
|
516
|
-
/** @type {ObjectPropertySchema} */
|
517
|
-
const sourceTypeSchema = {
|
518
|
-
merge: "replace",
|
519
|
-
validate(value) {
|
520
|
-
if (typeof value !== "string" || !/^(?:script|module|commonjs)$/u.test(value)) {
|
521
|
-
throw new TypeError("Expected \"script\", \"module\", or \"commonjs\".");
|
522
|
-
}
|
523
|
-
}
|
524
|
-
};
|
525
|
-
|
526
516
|
/**
|
527
517
|
* Creates a schema that always throws an error. Useful for warning
|
528
518
|
* about eslintrc-style keys.
|
@@ -568,15 +558,8 @@ const flatConfigSchema = {
|
|
568
558
|
reportUnusedDisableDirectives: disableDirectiveSeveritySchema
|
569
559
|
}
|
570
560
|
},
|
571
|
-
|
572
|
-
|
573
|
-
ecmaVersion: ecmaVersionSchema,
|
574
|
-
sourceType: sourceTypeSchema,
|
575
|
-
globals: globalsSchema,
|
576
|
-
parser: parserSchema,
|
577
|
-
parserOptions: deepObjectAssignSchema
|
578
|
-
}
|
579
|
-
},
|
561
|
+
language: languageSchema,
|
562
|
+
languageOptions: languageOptionsSchema,
|
580
563
|
processor: processorSchema,
|
581
564
|
plugins: pluginsSchema,
|
582
565
|
rules: rulesSchema
|
@@ -588,5 +571,6 @@ const flatConfigSchema = {
|
|
588
571
|
|
589
572
|
module.exports = {
|
590
573
|
flatConfigSchema,
|
574
|
+
hasMethod,
|
591
575
|
assertIsRuleSeverity
|
592
576
|
};
|
@@ -91,7 +91,7 @@ class AllFilesIgnoredError extends Error {
|
|
91
91
|
*/
|
92
92
|
constructor(pattern) {
|
93
93
|
super(`All files matched by '${pattern}' are ignored.`);
|
94
|
-
this.messageTemplate = "all-files-ignored";
|
94
|
+
this.messageTemplate = "all-matched-files-ignored";
|
95
95
|
this.messageData = { pattern };
|
96
96
|
}
|
97
97
|
}
|
@@ -494,7 +494,7 @@ async function globMultiSearch({ searches, configs, errorOnUnmatchedPattern }) {
|
|
494
494
|
|
495
495
|
}
|
496
496
|
|
497
|
-
return
|
497
|
+
return filePaths;
|
498
498
|
|
499
499
|
}
|
500
500
|
|
@@ -543,10 +543,7 @@ async function findFiles({
|
|
543
543
|
|
544
544
|
// files are added directly to the list
|
545
545
|
if (stat.isFile()) {
|
546
|
-
results.push(
|
547
|
-
filePath,
|
548
|
-
ignored: !configs.getConfig(filePath)
|
549
|
-
});
|
546
|
+
results.push(filePath);
|
550
547
|
}
|
551
548
|
|
552
549
|
// directories need extensions attached
|
@@ -604,11 +601,10 @@ async function findFiles({
|
|
604
601
|
});
|
605
602
|
|
606
603
|
return [
|
607
|
-
...
|
608
|
-
|
609
|
-
filePath
|
610
|
-
|
611
|
-
}))
|
604
|
+
...new Set([
|
605
|
+
...results,
|
606
|
+
...globbyResults.map(filePath => path.resolve(filePath))
|
607
|
+
])
|
612
608
|
];
|
613
609
|
}
|
614
610
|
|
@@ -630,17 +626,31 @@ function isErrorMessage(message) {
|
|
630
626
|
* Returns result with warning by ignore settings
|
631
627
|
* @param {string} filePath File path of checked code
|
632
628
|
* @param {string} baseDir Absolute path of base directory
|
629
|
+
* @param {"ignored"|"external"|"unconfigured"} configStatus A status that determines why the file is ignored
|
633
630
|
* @returns {LintResult} Result with single warning
|
634
631
|
* @private
|
635
632
|
*/
|
636
|
-
function createIgnoreResult(filePath, baseDir) {
|
633
|
+
function createIgnoreResult(filePath, baseDir, configStatus) {
|
637
634
|
let message;
|
638
|
-
const isInNodeModules = baseDir && path.dirname(path.relative(baseDir, filePath)).split(path.sep).includes("node_modules");
|
639
635
|
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
636
|
+
switch (configStatus) {
|
637
|
+
case "external":
|
638
|
+
message = "File ignored because outside of base path.";
|
639
|
+
break;
|
640
|
+
case "unconfigured":
|
641
|
+
message = "File ignored because no matching configuration was supplied.";
|
642
|
+
break;
|
643
|
+
default:
|
644
|
+
{
|
645
|
+
const isInNodeModules = baseDir && path.dirname(path.relative(baseDir, filePath)).split(path.sep).includes("node_modules");
|
646
|
+
|
647
|
+
if (isInNodeModules) {
|
648
|
+
message = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning.";
|
649
|
+
} else {
|
650
|
+
message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning.";
|
651
|
+
}
|
652
|
+
}
|
653
|
+
break;
|
644
654
|
}
|
645
655
|
|
646
656
|
return {
|
@@ -713,6 +723,7 @@ function processOptions({
|
|
713
723
|
errorOnUnmatchedPattern = true,
|
714
724
|
fix = false,
|
715
725
|
fixTypes = null, // ← should be null by default because if it's an array then it suppresses rules that don't have the `meta.type` property.
|
726
|
+
flags = [],
|
716
727
|
globInputPaths = true,
|
717
728
|
ignore = true,
|
718
729
|
ignorePatterns = null,
|
@@ -800,6 +811,9 @@ function processOptions({
|
|
800
811
|
if (fixTypes !== null && !isFixTypeArray(fixTypes)) {
|
801
812
|
errors.push("'fixTypes' must be an array of any of \"directive\", \"problem\", \"suggestion\", and \"layout\".");
|
802
813
|
}
|
814
|
+
if (!isEmptyArrayOrArrayOfNonEmptyString(flags)) {
|
815
|
+
errors.push("'flags' must be an array of non-empty strings.");
|
816
|
+
}
|
803
817
|
if (typeof globInputPaths !== "boolean") {
|
804
818
|
errors.push("'globInputPaths' must be a boolean.");
|
805
819
|
}
|
@@ -853,6 +867,7 @@ function processOptions({
|
|
853
867
|
errorOnUnmatchedPattern,
|
854
868
|
fix,
|
855
869
|
fixTypes,
|
870
|
+
flags: [...flags],
|
856
871
|
globInputPaths,
|
857
872
|
ignore,
|
858
873
|
ignorePatterns,
|
package/lib/eslint/eslint.js
CHANGED
@@ -68,7 +68,7 @@ const { Retrier } = require("@humanwhocodes/retry");
|
|
68
68
|
* The options with which to configure the ESLint instance.
|
69
69
|
* @typedef {Object} ESLintOptions
|
70
70
|
* @property {boolean} [allowInlineConfig] Enable or disable inline configuration comments.
|
71
|
-
* @property {ConfigData} [baseConfig] Base config
|
71
|
+
* @property {ConfigData|Array<ConfigData>} [baseConfig] Base config, extended by all configs used with this instance
|
72
72
|
* @property {boolean} [cache] Enable result caching.
|
73
73
|
* @property {string} [cacheLocation] The cache file to use instead of .eslintcache.
|
74
74
|
* @property {"metadata" | "content"} [cacheStrategy] The strategy used to detect changed files.
|
@@ -76,10 +76,11 @@ const { Retrier } = require("@humanwhocodes/retry");
|
|
76
76
|
* @property {boolean} [errorOnUnmatchedPattern] If `false` then `ESLint#lintFiles()` doesn't throw even if no target files found. Defaults to `true`.
|
77
77
|
* @property {boolean|Function} [fix] Execute in autofix mode. If a function, should return a boolean.
|
78
78
|
* @property {string[]} [fixTypes] Array of rule types to apply fixes for.
|
79
|
+
* @property {string[]} [flags] Array of feature flags to enable.
|
79
80
|
* @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file.
|
80
81
|
* @property {boolean} [ignore] False disables all ignore patterns except for the default ones.
|
81
82
|
* @property {string[]} [ignorePatterns] Ignore file patterns to use in addition to config ignores. These patterns are relative to `cwd`.
|
82
|
-
* @property {ConfigData} [overrideConfig] Override config
|
83
|
+
* @property {ConfigData|Array<ConfigData>} [overrideConfig] Override config, overrides all configs used with this instance
|
83
84
|
* @property {boolean|string} [overrideConfigFile] Searches for default config file when falsy;
|
84
85
|
* doesn't do any config file lookup when `true`; considered to be a config filename
|
85
86
|
* when a string.
|
@@ -421,7 +422,8 @@ async function calculateConfigArray(eslint, {
|
|
421
422
|
relativeIgnorePatterns = ignorePatterns;
|
422
423
|
} else {
|
423
424
|
|
424
|
-
|
425
|
+
// In minimatch patterns, only `/` can be used as path separator
|
426
|
+
const relativeIgnorePath = path.relative(basePath, cwd).replaceAll(path.sep, "/");
|
425
427
|
|
426
428
|
relativeIgnorePatterns = ignorePatterns.map(pattern => {
|
427
429
|
const negated = pattern.startsWith("!");
|
@@ -593,7 +595,8 @@ class ESLint {
|
|
593
595
|
const processedOptions = processOptions(options);
|
594
596
|
const linter = new Linter({
|
595
597
|
cwd: processedOptions.cwd,
|
596
|
-
configType: "flat"
|
598
|
+
configType: "flat",
|
599
|
+
flags: processedOptions.flags
|
597
600
|
});
|
598
601
|
|
599
602
|
const cacheFilePath = getCacheFile(
|
@@ -766,6 +769,17 @@ class ESLint {
|
|
766
769
|
return createRulesMeta(resultRules);
|
767
770
|
}
|
768
771
|
|
772
|
+
/**
|
773
|
+
* Indicates if the given feature flag is enabled for this instance.
|
774
|
+
* @param {string} flag The feature flag to check.
|
775
|
+
* @returns {boolean} `true` if the feature flag is enabled, `false` if not.
|
776
|
+
*/
|
777
|
+
hasFlag(flag) {
|
778
|
+
|
779
|
+
// note: Linter does validation of the flags
|
780
|
+
return privateMembers.get(this).linter.hasFlag(flag);
|
781
|
+
}
|
782
|
+
|
769
783
|
/**
|
770
784
|
* Executes the current configuration on an array of file and directory names.
|
771
785
|
* @param {string|string[]} patterns An array of file and directory names.
|
@@ -865,22 +879,24 @@ class ESLint {
|
|
865
879
|
*/
|
866
880
|
const results = await Promise.all(
|
867
881
|
|
868
|
-
filePaths.map(
|
882
|
+
filePaths.map(filePath => {
|
883
|
+
|
884
|
+
const config = configs.getConfig(filePath);
|
869
885
|
|
870
886
|
/*
|
871
|
-
* If a filename was entered that
|
872
|
-
*
|
887
|
+
* If a filename was entered that cannot be matched
|
888
|
+
* to a config, then notify the user.
|
873
889
|
*/
|
874
|
-
if (
|
890
|
+
if (!config) {
|
875
891
|
if (warnIgnored) {
|
876
|
-
|
892
|
+
const configStatus = configs.getConfigStatus(filePath);
|
893
|
+
|
894
|
+
return createIgnoreResult(filePath, cwd, configStatus);
|
877
895
|
}
|
878
896
|
|
879
897
|
return void 0;
|
880
898
|
}
|
881
899
|
|
882
|
-
const config = configs.getConfig(filePath);
|
883
|
-
|
884
900
|
// Skip if there is cached result.
|
885
901
|
if (lintResultCache) {
|
886
902
|
const cachedResult =
|
@@ -1029,7 +1045,9 @@ class ESLint {
|
|
1029
1045
|
const shouldWarnIgnored = typeof warnIgnored === "boolean" ? warnIgnored : constructorWarnIgnored;
|
1030
1046
|
|
1031
1047
|
if (shouldWarnIgnored) {
|
1032
|
-
|
1048
|
+
const configStatus = configs.getConfigStatus(resolvedFilename);
|
1049
|
+
|
1050
|
+
results.push(createIgnoreResult(resolvedFilename, cwd, configStatus));
|
1033
1051
|
}
|
1034
1052
|
} else {
|
1035
1053
|
|
@@ -171,6 +171,7 @@ function processOptions({
|
|
171
171
|
extensions = null, // ← should be null by default because if it's an array then it suppresses RFC20 feature.
|
172
172
|
fix = false,
|
173
173
|
fixTypes = null, // ← should be null by default because if it's an array then it suppresses rules that don't have the `meta.type` property.
|
174
|
+
flags, /* eslint-disable-line no-unused-vars -- leaving for compatibility with ESLint#hasFlag */
|
174
175
|
globInputPaths = true,
|
175
176
|
ignore = true,
|
176
177
|
ignorePath = null, // ← should be null by default because if it's a string then it may throw ENOENT.
|
@@ -310,6 +311,7 @@ function processOptions({
|
|
310
311
|
extensions,
|
311
312
|
fix,
|
312
313
|
fixTypes,
|
314
|
+
flags: [], // LegacyESLint does not support flags, so just ignore them.
|
313
315
|
globInputPaths,
|
314
316
|
ignore,
|
315
317
|
ignorePath,
|
@@ -558,6 +560,18 @@ class LegacyESLint {
|
|
558
560
|
|
559
561
|
}
|
560
562
|
|
563
|
+
/* eslint-disable no-unused-vars, class-methods-use-this -- leaving for compatibility with ESLint#hasFlag */
|
564
|
+
/**
|
565
|
+
* Indicates if the given feature flag is enabled for this instance. For this
|
566
|
+
* class, this always returns `false` because it does not support feature flags.
|
567
|
+
* @param {string} flag The feature flag to check.
|
568
|
+
* @returns {boolean} Always false.
|
569
|
+
*/
|
570
|
+
hasFlag(flag) {
|
571
|
+
return false;
|
572
|
+
}
|
573
|
+
/* eslint-enable no-unused-vars, class-methods-use-this -- reenable rules for the rest of the file */
|
574
|
+
|
561
575
|
/**
|
562
576
|
* Executes the current configuration on an array of file and directory names.
|
563
577
|
* @param {string[]} patterns An array of file and directory names.
|