eslint 9.7.0 → 9.9.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 +1 -1
- package/lib/cli.js +4 -23
- package/lib/eslint/eslint.js +93 -17
- package/lib/languages/js/source-code/source-code.js +19 -0
- package/lib/linter/apply-disable-directives.js +24 -12
- package/lib/linter/linter.js +44 -20
- package/lib/linter/report-translator.js +10 -10
- package/lib/linter/rule-fixer.js +38 -15
- package/lib/rules/require-await.js +37 -3
- package/lib/rules/utils/ast-utils.js +4 -3
- package/lib/shared/flags.js +3 -2
- package/package.json +18 -8
package/README.md
CHANGED
@@ -295,7 +295,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
|
|
295
295
|
<!--sponsorsstart-->
|
296
296
|
<h3>Platinum Sponsors</h3>
|
297
297
|
<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>
|
298
|
-
<p><a href="#"><img src="https://images.opencollective.com/guest-bf377e88/avatar.png" alt="Eli Schleifer" height="96"></a
|
298
|
+
<p><a href="#"><img src="https://images.opencollective.com/guest-bf377e88/avatar.png" alt="Eli Schleifer" 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/lib/cli.js
CHANGED
@@ -26,7 +26,6 @@ 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
|
-
const { inactiveFlags, activeFlags } = require("./shared/flags");
|
30
29
|
const debug = require("debug")("eslint:cli");
|
31
30
|
|
32
31
|
//------------------------------------------------------------------------------
|
@@ -341,16 +340,17 @@ const cli = {
|
|
341
340
|
/**
|
342
341
|
* Calculates the command string for the --inspect-config operation.
|
343
342
|
* @param {string} configFile The path to the config file to inspect.
|
343
|
+
* @param {boolean} hasUnstableTSConfigFlag `true` if the `unstable_ts_config` flag is enabled, `false` if it's not.
|
344
344
|
* @returns {Promise<string>} The command string to execute.
|
345
345
|
*/
|
346
|
-
async calculateInspectConfigFlags(configFile) {
|
346
|
+
async calculateInspectConfigFlags(configFile, hasUnstableTSConfigFlag) {
|
347
347
|
|
348
348
|
// find the config file
|
349
349
|
const {
|
350
350
|
configFilePath,
|
351
351
|
basePath,
|
352
352
|
error
|
353
|
-
} = await locateConfigFileToUse({ cwd: process.cwd(), configFile });
|
353
|
+
} = await locateConfigFileToUse({ cwd: process.cwd(), configFile }, hasUnstableTSConfigFlag);
|
354
354
|
|
355
355
|
if (error) {
|
356
356
|
throw error;
|
@@ -455,7 +455,7 @@ const cli = {
|
|
455
455
|
try {
|
456
456
|
const flatOptions = await translateOptions(options, "flat");
|
457
457
|
const spawn = require("cross-spawn");
|
458
|
-
const flags = await cli.calculateInspectConfigFlags(flatOptions.overrideConfigFile);
|
458
|
+
const flags = await cli.calculateInspectConfigFlags(flatOptions.overrideConfigFile, flatOptions.flags ? flatOptions.flags.includes("unstable_ts_config") : false);
|
459
459
|
|
460
460
|
spawn.sync("npx", ["@eslint/config-inspector@latest", ...flags], { encoding: "utf8", stdio: "inherit" });
|
461
461
|
} catch (error) {
|
@@ -488,25 +488,6 @@ const cli = {
|
|
488
488
|
|
489
489
|
const ActiveESLint = usingFlatConfig ? ESLint : LegacyESLint;
|
490
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
|
-
}
|
509
|
-
|
510
491
|
const engine = new ActiveESLint(eslintOptions);
|
511
492
|
let results;
|
512
493
|
|
package/lib/eslint/eslint.js
CHANGED
@@ -63,6 +63,8 @@ const { Retrier } = require("@humanwhocodes/retry");
|
|
63
63
|
/** @typedef {import("../shared/types").RuleConf} RuleConf */
|
64
64
|
/** @typedef {import("../shared/types").Rule} Rule */
|
65
65
|
/** @typedef {ReturnType<ConfigArray.extractConfig>} ExtractedConfig */
|
66
|
+
/** @typedef {import('../cli-engine/cli-engine').CLIEngine} CLIEngine */
|
67
|
+
/** @typedef {import('./legacy-eslint').CLIEngineLintReport} CLIEngineLintReport */
|
66
68
|
|
67
69
|
/**
|
68
70
|
* The options with which to configure the ESLint instance.
|
@@ -86,7 +88,7 @@ const { Retrier } = require("@humanwhocodes/retry");
|
|
86
88
|
* when a string.
|
87
89
|
* @property {Record<string,Plugin>} [plugins] An array of plugin implementations.
|
88
90
|
* @property {boolean} [stats] True enables added statistics on lint results.
|
89
|
-
* @property {boolean} warnIgnored Show warnings when the file list includes ignored files
|
91
|
+
* @property {boolean} [warnIgnored] Show warnings when the file list includes ignored files
|
90
92
|
* @property {boolean} [passOnNoPatterns=false] When set to true, missing patterns cause
|
91
93
|
* the linting operation to short circuit and not report any failures.
|
92
94
|
*/
|
@@ -100,8 +102,18 @@ const FLAT_CONFIG_FILENAMES = [
|
|
100
102
|
"eslint.config.mjs",
|
101
103
|
"eslint.config.cjs"
|
102
104
|
];
|
105
|
+
const FLAT_CONFIG_FILENAMES_WITH_TS = [
|
106
|
+
...FLAT_CONFIG_FILENAMES,
|
107
|
+
"eslint.config.ts",
|
108
|
+
"eslint.config.mts",
|
109
|
+
"eslint.config.cts"
|
110
|
+
];
|
103
111
|
const debug = require("debug")("eslint:eslint");
|
104
112
|
const privateMembers = new WeakMap();
|
113
|
+
|
114
|
+
/**
|
115
|
+
* @type {Map<string, string>}
|
116
|
+
*/
|
105
117
|
const importedConfigFileModificationTime = new Map();
|
106
118
|
const removedFormatters = new Set([
|
107
119
|
"checkstyle",
|
@@ -262,28 +274,59 @@ function compareResultsByFilePath(a, b) {
|
|
262
274
|
* Searches from the current working directory up until finding the
|
263
275
|
* given flat config filename.
|
264
276
|
* @param {string} cwd The current working directory to search from.
|
277
|
+
* @param {boolean} hasUnstableTSConfigFlag `true` if the `unstable_ts_config` flag is enabled, `false` if it's not.
|
265
278
|
* @returns {Promise<string|undefined>} The filename if found or `undefined` if not.
|
266
279
|
*/
|
267
|
-
function findFlatConfigFile(cwd) {
|
280
|
+
function findFlatConfigFile(cwd, hasUnstableTSConfigFlag) {
|
281
|
+
const filenames = hasUnstableTSConfigFlag ? FLAT_CONFIG_FILENAMES_WITH_TS : FLAT_CONFIG_FILENAMES;
|
282
|
+
|
268
283
|
return findUp(
|
269
|
-
|
284
|
+
filenames,
|
270
285
|
{ cwd }
|
271
286
|
);
|
272
287
|
}
|
273
288
|
|
289
|
+
/**
|
290
|
+
* Check if the file is a TypeScript file.
|
291
|
+
* @param {string} filePath The file path to check.
|
292
|
+
* @returns {boolean} `true` if the file is a TypeScript file, `false` if it's not.
|
293
|
+
*/
|
294
|
+
function isFileTS(filePath) {
|
295
|
+
const fileExtension = path.extname(filePath);
|
296
|
+
|
297
|
+
return /^\.[mc]?ts$/u.test(fileExtension);
|
298
|
+
}
|
299
|
+
|
300
|
+
/**
|
301
|
+
* Check if ESLint is running in Bun.
|
302
|
+
* @returns {boolean} `true` if the ESLint is running Bun, `false` if it's not.
|
303
|
+
*/
|
304
|
+
function isRunningInBun() {
|
305
|
+
return !!globalThis.Bun;
|
306
|
+
}
|
307
|
+
|
308
|
+
/**
|
309
|
+
* Check if ESLint is running in Deno.
|
310
|
+
* @returns {boolean} `true` if the ESLint is running in Deno, `false` if it's not.
|
311
|
+
*/
|
312
|
+
function isRunningInDeno() {
|
313
|
+
return !!globalThis.Deno;
|
314
|
+
}
|
315
|
+
|
274
316
|
/**
|
275
317
|
* Load the config array from the given filename.
|
276
318
|
* @param {string} filePath The filename to load from.
|
319
|
+
* @param {boolean} hasUnstableTSConfigFlag `true` if the `unstable_ts_config` flag is enabled, `false` if it's not.
|
277
320
|
* @returns {Promise<any>} The config loaded from the config file.
|
278
321
|
*/
|
279
|
-
async function loadFlatConfigFile(filePath) {
|
322
|
+
async function loadFlatConfigFile(filePath, hasUnstableTSConfigFlag) {
|
280
323
|
debug(`Loading config from ${filePath}`);
|
281
324
|
|
282
325
|
const fileURL = pathToFileURL(filePath);
|
283
326
|
|
284
327
|
debug(`Config file URL is ${fileURL}`);
|
285
328
|
|
286
|
-
const mtime = (await fs.stat(filePath)).mtime.getTime();
|
329
|
+
const mtime = (await fs.stat(filePath)).mtime.getTime().toString();
|
287
330
|
|
288
331
|
/*
|
289
332
|
* Append a query with the config file's modification time (`mtime`) in order
|
@@ -314,7 +357,37 @@ async function loadFlatConfigFile(filePath) {
|
|
314
357
|
delete require.cache[filePath];
|
315
358
|
}
|
316
359
|
|
317
|
-
const
|
360
|
+
const isTS = isFileTS(filePath) && hasUnstableTSConfigFlag;
|
361
|
+
|
362
|
+
const isBun = isRunningInBun();
|
363
|
+
|
364
|
+
const isDeno = isRunningInDeno();
|
365
|
+
|
366
|
+
if (isTS && !isDeno && !isBun) {
|
367
|
+
|
368
|
+
const createJiti = await import("jiti").then(jitiModule => jitiModule.default, () => {
|
369
|
+
throw new Error("The 'jiti' library is required for loading TypeScript configuration files. Make sure to install it.");
|
370
|
+
});
|
371
|
+
|
372
|
+
/*
|
373
|
+
* Disabling `moduleCache` allows us to reload a
|
374
|
+
* config file when the last modified timestamp changes.
|
375
|
+
*/
|
376
|
+
|
377
|
+
const jiti = createJiti(__filename, { moduleCache: false });
|
378
|
+
|
379
|
+
if (typeof jiti?.import !== "function") {
|
380
|
+
throw new Error("You are using an outdated version of the 'jiti' library. Please update to the latest version of 'jiti' to ensure compatibility and access to the latest features.");
|
381
|
+
}
|
382
|
+
|
383
|
+
const config = await jiti.import(fileURL.href);
|
384
|
+
|
385
|
+
importedConfigFileModificationTime.set(filePath, mtime);
|
386
|
+
|
387
|
+
return config?.default ?? config;
|
388
|
+
}
|
389
|
+
|
390
|
+
const config = (await import(fileURL.href)).default;
|
318
391
|
|
319
392
|
importedConfigFileModificationTime.set(filePath, mtime);
|
320
393
|
|
@@ -326,11 +399,12 @@ async function loadFlatConfigFile(filePath) {
|
|
326
399
|
* override config file was passed, and if so, using it; otherwise, as long
|
327
400
|
* as override config file is not explicitly set to `false`, it will search
|
328
401
|
* upwards from the cwd for a file named `eslint.config.js`.
|
329
|
-
* @param {
|
330
|
-
* @
|
402
|
+
* @param {ESLintOptions} options The ESLint instance options.
|
403
|
+
* @param {boolean} hasUnstableTSConfigFlag `true` if the `unstable_ts_config` flag is enabled, `false` if it's not.
|
404
|
+
* @returns {Promise<{configFilePath:string|undefined;basePath:string;error:Error|null}>} Location information for
|
331
405
|
* the config file.
|
332
406
|
*/
|
333
|
-
async function locateConfigFileToUse({ configFile, cwd }) {
|
407
|
+
async function locateConfigFileToUse({ configFile, cwd }, hasUnstableTSConfigFlag) {
|
334
408
|
|
335
409
|
// determine where to load config file from
|
336
410
|
let configFilePath;
|
@@ -342,7 +416,7 @@ async function locateConfigFileToUse({ configFile, cwd }) {
|
|
342
416
|
configFilePath = path.resolve(cwd, configFile);
|
343
417
|
} else if (configFile !== false) {
|
344
418
|
debug("Searching for eslint.config.js");
|
345
|
-
configFilePath = await findFlatConfigFile(cwd);
|
419
|
+
configFilePath = await findFlatConfigFile(cwd, hasUnstableTSConfigFlag);
|
346
420
|
|
347
421
|
if (configFilePath) {
|
348
422
|
basePath = path.resolve(path.dirname(configFilePath));
|
@@ -364,8 +438,8 @@ async function locateConfigFileToUse({ configFile, cwd }) {
|
|
364
438
|
/**
|
365
439
|
* Calculates the config array for this run based on inputs.
|
366
440
|
* @param {ESLint} eslint The instance to create the config array for.
|
367
|
-
* @param {
|
368
|
-
* @returns {FlatConfigArray} The config array for `eslint``.
|
441
|
+
* @param {ESLintOptions} options The ESLint instance options.
|
442
|
+
* @returns {Promise<typeof FlatConfigArray>} The config array for `eslint``.
|
369
443
|
*/
|
370
444
|
async function calculateConfigArray(eslint, {
|
371
445
|
cwd,
|
@@ -383,7 +457,9 @@ async function calculateConfigArray(eslint, {
|
|
383
457
|
return slots.configs;
|
384
458
|
}
|
385
459
|
|
386
|
-
const
|
460
|
+
const hasUnstableTSConfigFlag = eslint.hasFlag("unstable_ts_config");
|
461
|
+
|
462
|
+
const { configFilePath, basePath, error } = await locateConfigFileToUse({ configFile, cwd }, hasUnstableTSConfigFlag);
|
387
463
|
|
388
464
|
// config file is required to calculate config
|
389
465
|
if (error) {
|
@@ -394,7 +470,7 @@ async function calculateConfigArray(eslint, {
|
|
394
470
|
|
395
471
|
// load config file
|
396
472
|
if (configFilePath) {
|
397
|
-
const fileConfig = await loadFlatConfigFile(configFilePath);
|
473
|
+
const fileConfig = await loadFlatConfigFile(configFilePath, hasUnstableTSConfigFlag);
|
398
474
|
|
399
475
|
if (Array.isArray(fileConfig)) {
|
400
476
|
configs.push(...fileConfig);
|
@@ -1144,7 +1220,7 @@ class ESLint {
|
|
1144
1220
|
|
1145
1221
|
/**
|
1146
1222
|
* The main formatter method.
|
1147
|
-
* @param {
|
1223
|
+
* @param {LintResult[]} results The lint results to format.
|
1148
1224
|
* @param {ResultsMeta} resultsMeta Warning count and max threshold.
|
1149
1225
|
* @returns {string} The formatted lint results.
|
1150
1226
|
*/
|
@@ -1190,12 +1266,12 @@ class ESLint {
|
|
1190
1266
|
/**
|
1191
1267
|
* Finds the config file being used by this instance based on the options
|
1192
1268
|
* passed to the constructor.
|
1193
|
-
* @returns {string|undefined} The path to the config file being used or
|
1269
|
+
* @returns {Promise<string|undefined>} The path to the config file being used or
|
1194
1270
|
* `undefined` if no config file is being used.
|
1195
1271
|
*/
|
1196
1272
|
async findConfigFile() {
|
1197
1273
|
const options = privateMembers.get(this).options;
|
1198
|
-
const { configFilePath } = await locateConfigFileToUse(options);
|
1274
|
+
const { configFilePath } = await locateConfigFileToUse(options, this.hasFlag("unstable_ts_config"));
|
1199
1275
|
|
1200
1276
|
return configFilePath;
|
1201
1277
|
}
|
@@ -916,6 +916,25 @@ class SourceCode extends TokenStore {
|
|
916
916
|
|
917
917
|
return ancestorsStartingAtParent.reverse();
|
918
918
|
}
|
919
|
+
|
920
|
+
/**
|
921
|
+
* Returns the location of the given node or token.
|
922
|
+
* @param {ASTNode|Token} nodeOrToken The node or token to get the location of.
|
923
|
+
* @returns {SourceLocation} The location of the node or token.
|
924
|
+
*/
|
925
|
+
getLoc(nodeOrToken) {
|
926
|
+
return nodeOrToken.loc;
|
927
|
+
}
|
928
|
+
|
929
|
+
/**
|
930
|
+
* Returns the range of the given node or token.
|
931
|
+
* @param {ASTNode|Token} nodeOrToken The node or token to get the range of.
|
932
|
+
* @returns {[number, number]} The range of the node or token.
|
933
|
+
*/
|
934
|
+
getRange(nodeOrToken) {
|
935
|
+
return nodeOrToken.range;
|
936
|
+
}
|
937
|
+
|
919
938
|
/* eslint-enable class-methods-use-this -- node is owned by SourceCode */
|
920
939
|
|
921
940
|
/**
|
@@ -61,15 +61,18 @@ function groupByParentDirective(directives) {
|
|
61
61
|
* Creates removal details for a set of directives within the same comment.
|
62
62
|
* @param {Directive[]} directives Unused directives to be removed.
|
63
63
|
* @param {Token} node The backing Comment token.
|
64
|
+
* @param {SourceCode} sourceCode The source code object for the file being linted.
|
64
65
|
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
|
65
66
|
*/
|
66
|
-
function createIndividualDirectivesRemoval(directives, node) {
|
67
|
+
function createIndividualDirectivesRemoval(directives, node, sourceCode) {
|
68
|
+
|
69
|
+
const range = sourceCode.getRange(node);
|
67
70
|
|
68
71
|
/*
|
69
72
|
* `node.value` starts right after `//` or `/*`.
|
70
73
|
* All calculated offsets will be relative to this index.
|
71
74
|
*/
|
72
|
-
const commentValueStart =
|
75
|
+
const commentValueStart = range[0] + "//".length;
|
73
76
|
|
74
77
|
// Find where the list of rules starts. `\S+` matches with the directive name (e.g. `eslint-disable-line`)
|
75
78
|
const listStartOffset = /^\s*\S+\s+/u.exec(node.value)[0].length;
|
@@ -165,10 +168,11 @@ function createIndividualDirectivesRemoval(directives, node) {
|
|
165
168
|
* Creates a description of deleting an entire unused disable directive.
|
166
169
|
* @param {Directive[]} directives Unused directives to be removed.
|
167
170
|
* @param {Token} node The backing Comment token.
|
171
|
+
* @param {SourceCode} sourceCode The source code object for the file being linted.
|
168
172
|
* @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output problem.
|
169
173
|
*/
|
170
|
-
function createDirectiveRemoval(directives, node) {
|
171
|
-
const
|
174
|
+
function createDirectiveRemoval(directives, node, sourceCode) {
|
175
|
+
const range = sourceCode.getRange(node);
|
172
176
|
const ruleIds = directives.filter(directive => directive.ruleId).map(directive => `'${directive.ruleId}'`);
|
173
177
|
|
174
178
|
return {
|
@@ -186,9 +190,10 @@ function createDirectiveRemoval(directives, node) {
|
|
186
190
|
/**
|
187
191
|
* Parses details from directives to create output Problems.
|
188
192
|
* @param {Iterable<Directive>} allDirectives Unused directives to be removed.
|
193
|
+
* @param {SourceCode} sourceCode The source code object for the file being linted.
|
189
194
|
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
|
190
195
|
*/
|
191
|
-
function processUnusedDirectives(allDirectives) {
|
196
|
+
function processUnusedDirectives(allDirectives, sourceCode) {
|
192
197
|
const directiveGroups = groupByParentDirective(allDirectives);
|
193
198
|
|
194
199
|
return directiveGroups.flatMap(
|
@@ -201,8 +206,8 @@ function processUnusedDirectives(allDirectives) {
|
|
201
206
|
}
|
202
207
|
|
203
208
|
return remainingRuleIds.size
|
204
|
-
? createIndividualDirectivesRemoval(directives, parentDirective.node)
|
205
|
-
: [createDirectiveRemoval(directives, parentDirective.node)];
|
209
|
+
? createIndividualDirectivesRemoval(directives, parentDirective.node, sourceCode)
|
210
|
+
: [createDirectiveRemoval(directives, parentDirective.node, sourceCode)];
|
206
211
|
}
|
207
212
|
);
|
208
213
|
}
|
@@ -309,6 +314,7 @@ function collectUsedEnableDirectives(directives) {
|
|
309
314
|
function applyDirectives(options) {
|
310
315
|
const problems = [];
|
311
316
|
const usedDisableDirectives = new Set();
|
317
|
+
const { sourceCode } = options;
|
312
318
|
|
313
319
|
for (const problem of options.problems) {
|
314
320
|
let disableDirectivesForProblem = [];
|
@@ -370,8 +376,8 @@ function applyDirectives(options) {
|
|
370
376
|
}
|
371
377
|
}
|
372
378
|
|
373
|
-
const processed = processUnusedDirectives(unusedDisableDirectivesToReport)
|
374
|
-
.concat(processUnusedDirectives(unusedEnableDirectivesToReport));
|
379
|
+
const processed = processUnusedDirectives(unusedDisableDirectivesToReport, sourceCode)
|
380
|
+
.concat(processUnusedDirectives(unusedEnableDirectivesToReport, sourceCode));
|
375
381
|
const columnOffset = options.language.columnStart === 1 ? 0 : 1;
|
376
382
|
const lineOffset = options.language.lineStart === 1 ? 0 : 1;
|
377
383
|
|
@@ -390,11 +396,14 @@ function applyDirectives(options) {
|
|
390
396
|
? `Unused eslint-disable directive (no problems were reported from ${description}).`
|
391
397
|
: "Unused eslint-disable directive (no problems were reported).";
|
392
398
|
}
|
399
|
+
|
400
|
+
const loc = sourceCode.getLoc(parentDirective.node);
|
401
|
+
|
393
402
|
return {
|
394
403
|
ruleId: null,
|
395
404
|
message,
|
396
|
-
line: type === "disable-next-line" ?
|
397
|
-
column: type === "disable-next-line" ?
|
405
|
+
line: type === "disable-next-line" ? loc.start.line + lineOffset : line,
|
406
|
+
column: type === "disable-next-line" ? loc.start.column + columnOffset : column,
|
398
407
|
severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,
|
399
408
|
nodeType: null,
|
400
409
|
...options.disableFixes ? {} : { fix }
|
@@ -409,6 +418,7 @@ function applyDirectives(options) {
|
|
409
418
|
* of reported problems, adds the suppression information to the problems.
|
410
419
|
* @param {Object} options Information about directives and problems
|
411
420
|
* @param {Language} options.language The language being linted.
|
421
|
+
* @param {SourceCode} options.sourceCode The source code object for the file being linted.
|
412
422
|
* @param {{
|
413
423
|
* type: ("disable"|"enable"|"disable-line"|"disable-next-line"),
|
414
424
|
* ruleId: (string|null),
|
@@ -427,7 +437,7 @@ function applyDirectives(options) {
|
|
427
437
|
* @returns {{ruleId: (string|null), line: number, column: number, suppressions?: {kind: string, justification: string}}[]}
|
428
438
|
* An object with a list of reported problems, the suppressed of which contain the suppression information.
|
429
439
|
*/
|
430
|
-
module.exports = ({ language, directives, disableFixes, problems, configuredRules, ruleFilter, reportUnusedDisableDirectives = "off" }) => {
|
440
|
+
module.exports = ({ language, sourceCode, directives, disableFixes, problems, configuredRules, ruleFilter, reportUnusedDisableDirectives = "off" }) => {
|
431
441
|
const blockDirectives = directives
|
432
442
|
.filter(directive => directive.type === "disable" || directive.type === "enable")
|
433
443
|
.map(directive => Object.assign({}, directive, { unprocessedDirective: directive }))
|
@@ -477,6 +487,7 @@ module.exports = ({ language, directives, disableFixes, problems, configuredRule
|
|
477
487
|
|
478
488
|
const blockDirectivesResult = applyDirectives({
|
479
489
|
language,
|
490
|
+
sourceCode,
|
480
491
|
problems,
|
481
492
|
directives: blockDirectives,
|
482
493
|
disableFixes,
|
@@ -485,6 +496,7 @@ module.exports = ({ language, directives, disableFixes, problems, configuredRule
|
|
485
496
|
});
|
486
497
|
const lineDirectivesResult = applyDirectives({
|
487
498
|
language,
|
499
|
+
sourceCode,
|
488
500
|
problems: blockDirectivesResult.problems,
|
489
501
|
directives: lineDirectives,
|
490
502
|
disableFixes,
|
package/lib/linter/linter.js
CHANGED
@@ -45,7 +45,7 @@ const { RuleValidator } = require("../config/rule-validator");
|
|
45
45
|
const { assertIsRuleSeverity } = require("../config/flat-config-schema");
|
46
46
|
const { normalizeSeverityToString } = require("../shared/severity");
|
47
47
|
const jslang = require("../languages/js");
|
48
|
-
const { activeFlags } = require("../shared/flags");
|
48
|
+
const { activeFlags, inactiveFlags } = require("../shared/flags");
|
49
49
|
const debug = require("debug")("eslint:linter");
|
50
50
|
const MAX_AUTOFIX_PASSES = 10;
|
51
51
|
const DEFAULT_PARSER_NAME = "espree";
|
@@ -323,9 +323,10 @@ function createLintingProblem(options) {
|
|
323
323
|
* @param {ASTNode|token} options.node The Comment node/token.
|
324
324
|
* @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
|
325
325
|
* @param {Language} language The language to use to adjust the location information.
|
326
|
+
* @param {SourceCode} sourceCode The SourceCode object to get comments from.
|
326
327
|
* @returns {Object} Directives and problems from the comment
|
327
328
|
*/
|
328
|
-
function createDisableDirectives({ type, value, justification, node }, ruleMapper, language) {
|
329
|
+
function createDisableDirectives({ type, value, justification, node }, ruleMapper, language, sourceCode) {
|
329
330
|
const ruleIds = Object.keys(commentParser.parseListConfig(value));
|
330
331
|
const directiveRules = ruleIds.length ? ruleIds : [null];
|
331
332
|
const result = {
|
@@ -336,11 +337,15 @@ function createDisableDirectives({ type, value, justification, node }, ruleMappe
|
|
336
337
|
|
337
338
|
for (const ruleId of directiveRules) {
|
338
339
|
|
340
|
+
const loc = sourceCode.getLoc(node);
|
341
|
+
|
339
342
|
// push to directives, if the rule is defined(including null, e.g. /*eslint enable*/)
|
340
343
|
if (ruleId === null || !!ruleMapper(ruleId)) {
|
344
|
+
|
345
|
+
|
341
346
|
if (type === "disable-next-line") {
|
342
347
|
const { line, column } = updateLocationInformation(
|
343
|
-
|
348
|
+
loc.end,
|
344
349
|
language
|
345
350
|
);
|
346
351
|
|
@@ -354,7 +359,7 @@ function createDisableDirectives({ type, value, justification, node }, ruleMappe
|
|
354
359
|
});
|
355
360
|
} else {
|
356
361
|
const { line, column } = updateLocationInformation(
|
357
|
-
|
362
|
+
loc.start,
|
358
363
|
language
|
359
364
|
);
|
360
365
|
|
@@ -368,7 +373,7 @@ function createDisableDirectives({ type, value, justification, node }, ruleMappe
|
|
368
373
|
});
|
369
374
|
}
|
370
375
|
} else {
|
371
|
-
result.directiveProblems.push(createLintingProblem({ ruleId, loc
|
376
|
+
result.directiveProblems.push(createLintingProblem({ ruleId, loc, language }));
|
372
377
|
}
|
373
378
|
}
|
374
379
|
return result;
|
@@ -410,25 +415,27 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
410
415
|
return;
|
411
416
|
}
|
412
417
|
|
418
|
+
const loc = sourceCode.getLoc(comment);
|
419
|
+
|
413
420
|
if (warnInlineConfig) {
|
414
421
|
const kind = comment.type === "Block" ? `/*${directiveText}*/` : `//${directiveText}`;
|
415
422
|
|
416
423
|
problems.push(createLintingProblem({
|
417
424
|
ruleId: null,
|
418
425
|
message: `'${kind}' has no effect because you have 'noInlineConfig' setting in ${warnInlineConfig}.`,
|
419
|
-
loc
|
426
|
+
loc,
|
420
427
|
severity: 1
|
421
428
|
}));
|
422
429
|
return;
|
423
430
|
}
|
424
431
|
|
425
|
-
if (directiveText === "eslint-disable-line" &&
|
432
|
+
if (directiveText === "eslint-disable-line" && loc.start.line !== loc.end.line) {
|
426
433
|
const message = `${directiveText} comment should not span multiple lines.`;
|
427
434
|
|
428
435
|
problems.push(createLintingProblem({
|
429
436
|
ruleId: null,
|
430
437
|
message,
|
431
|
-
loc
|
438
|
+
loc
|
432
439
|
}));
|
433
440
|
return;
|
434
441
|
}
|
@@ -446,7 +453,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
446
453
|
value: directiveValue,
|
447
454
|
justification: justificationPart,
|
448
455
|
node: comment
|
449
|
-
}, ruleMapper, jslang);
|
456
|
+
}, ruleMapper, jslang, sourceCode);
|
450
457
|
|
451
458
|
disableDirectives.push(...directives);
|
452
459
|
problems.push(...directiveProblems);
|
@@ -467,7 +474,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
467
474
|
} catch (err) {
|
468
475
|
problems.push(createLintingProblem({
|
469
476
|
ruleId: null,
|
470
|
-
loc
|
477
|
+
loc,
|
471
478
|
message: err.message
|
472
479
|
}));
|
473
480
|
continue;
|
@@ -494,14 +501,14 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
494
501
|
const ruleValue = parseResult.config[name];
|
495
502
|
|
496
503
|
if (!rule) {
|
497
|
-
problems.push(createLintingProblem({ ruleId: name, loc
|
504
|
+
problems.push(createLintingProblem({ ruleId: name, loc }));
|
498
505
|
return;
|
499
506
|
}
|
500
507
|
|
501
508
|
if (Object.hasOwn(configuredRules, name)) {
|
502
509
|
problems.push(createLintingProblem({
|
503
510
|
message: `Rule "${name}" is already configured by another configuration comment in the preceding code. This configuration is ignored.`,
|
504
|
-
loc
|
511
|
+
loc
|
505
512
|
}));
|
506
513
|
return;
|
507
514
|
}
|
@@ -563,7 +570,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
563
570
|
problems.push(createLintingProblem({
|
564
571
|
ruleId: name,
|
565
572
|
message: err.message,
|
566
|
-
loc
|
573
|
+
loc
|
567
574
|
}));
|
568
575
|
|
569
576
|
// do not apply the config, if found invalid options.
|
@@ -575,7 +582,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
575
582
|
} else {
|
576
583
|
const problem = createLintingProblem({
|
577
584
|
ruleId: null,
|
578
|
-
loc
|
585
|
+
loc,
|
579
586
|
message: parseResult.error.message
|
580
587
|
});
|
581
588
|
|
@@ -623,7 +630,7 @@ function getDirectiveCommentsForFlatConfig(sourceCode, ruleMapper, language) {
|
|
623
630
|
})));
|
624
631
|
|
625
632
|
directivesSources.forEach(directive => {
|
626
|
-
const { directives, directiveProblems } = createDisableDirectives(directive, ruleMapper, language);
|
633
|
+
const { directives, directiveProblems } = createDisableDirectives(directive, ruleMapper, language, sourceCode);
|
627
634
|
|
628
635
|
disableDirectives.push(...directives);
|
629
636
|
problems.push(...directiveProblems);
|
@@ -1282,9 +1289,20 @@ class Linter {
|
|
1282
1289
|
* @param {"flat"|"eslintrc"} [config.configType="flat"] the type of config used.
|
1283
1290
|
*/
|
1284
1291
|
constructor({ cwd, configType = "flat", flags = [] } = {}) {
|
1292
|
+
|
1293
|
+
flags.forEach(flag => {
|
1294
|
+
if (inactiveFlags.has(flag)) {
|
1295
|
+
throw new Error(`The flag '${flag}' is inactive: ${inactiveFlags.get(flag)}`);
|
1296
|
+
}
|
1297
|
+
|
1298
|
+
if (!activeFlags.has(flag)) {
|
1299
|
+
throw new Error(`Unknown flag '${flag}'.`);
|
1300
|
+
}
|
1301
|
+
});
|
1302
|
+
|
1285
1303
|
internalSlotsMap.set(this, {
|
1286
1304
|
cwd: normalizeCwd(cwd),
|
1287
|
-
flags
|
1305
|
+
flags,
|
1288
1306
|
lastConfigArray: null,
|
1289
1307
|
lastSourceCode: null,
|
1290
1308
|
lastSuppressedMessages: [],
|
@@ -1462,7 +1480,7 @@ class Linter {
|
|
1462
1480
|
debug("An error occurred while traversing");
|
1463
1481
|
debug("Filename:", options.filename);
|
1464
1482
|
if (err.currentNode) {
|
1465
|
-
const { line } = err.currentNode.
|
1483
|
+
const { line } = sourceCode.getLoc(err.currentNode).start;
|
1466
1484
|
|
1467
1485
|
debug("Line:", line);
|
1468
1486
|
err.message += `:${line}`;
|
@@ -1480,6 +1498,7 @@ class Linter {
|
|
1480
1498
|
|
1481
1499
|
return applyDisableDirectives({
|
1482
1500
|
language: jslang,
|
1501
|
+
sourceCode,
|
1483
1502
|
directives: commentDirectives.disableDirectives,
|
1484
1503
|
disableFixes: options.disableFixes,
|
1485
1504
|
problems: lintingProblems
|
@@ -1759,10 +1778,14 @@ class Linter {
|
|
1759
1778
|
if (options.warnInlineConfig) {
|
1760
1779
|
if (sourceCode.getInlineConfigNodes) {
|
1761
1780
|
sourceCode.getInlineConfigNodes().forEach(node => {
|
1781
|
+
|
1782
|
+
const loc = sourceCode.getLoc(node);
|
1783
|
+
const range = sourceCode.getRange(node);
|
1784
|
+
|
1762
1785
|
inlineConfigProblems.push(createLintingProblem({
|
1763
1786
|
ruleId: null,
|
1764
|
-
message: `'${sourceCode.text.slice(
|
1765
|
-
loc
|
1787
|
+
message: `'${sourceCode.text.slice(range[0], range[1])}' has no effect because you have 'noInlineConfig' setting in ${options.warnInlineConfig}.`,
|
1788
|
+
loc,
|
1766
1789
|
severity: 1,
|
1767
1790
|
language: config.language
|
1768
1791
|
}));
|
@@ -1942,7 +1965,7 @@ class Linter {
|
|
1942
1965
|
debug("An error occurred while traversing");
|
1943
1966
|
debug("Filename:", options.filename);
|
1944
1967
|
if (err.currentNode) {
|
1945
|
-
const { line } = err.currentNode.
|
1968
|
+
const { line } = sourceCode.getLoc(err.currentNode).start;
|
1946
1969
|
|
1947
1970
|
debug("Line:", line);
|
1948
1971
|
err.message += `:${line}`;
|
@@ -1961,6 +1984,7 @@ class Linter {
|
|
1961
1984
|
|
1962
1985
|
return applyDisableDirectives({
|
1963
1986
|
language: config.language,
|
1987
|
+
sourceCode,
|
1964
1988
|
directives: commentDirectives.disableDirectives,
|
1965
1989
|
disableFixes: options.disableFixes,
|
1966
1990
|
problems: lintingProblems
|
@@ -10,7 +10,7 @@
|
|
10
10
|
//------------------------------------------------------------------------------
|
11
11
|
|
12
12
|
const assert = require("node:assert");
|
13
|
-
const
|
13
|
+
const { RuleFixer } = require("./rule-fixer");
|
14
14
|
const { interpolate } = require("./interpolate");
|
15
15
|
|
16
16
|
//------------------------------------------------------------------------------
|
@@ -91,13 +91,10 @@ function assertValidNodeInfo(descriptor) {
|
|
91
91
|
* from the `node` of the original descriptor, or infers the `start` from the `loc` of the original descriptor.
|
92
92
|
*/
|
93
93
|
function normalizeReportLoc(descriptor) {
|
94
|
-
if (descriptor.loc) {
|
95
|
-
|
96
|
-
return descriptor.loc;
|
97
|
-
}
|
98
|
-
return { start: descriptor.loc, end: null };
|
94
|
+
if (descriptor.loc.start) {
|
95
|
+
return descriptor.loc;
|
99
96
|
}
|
100
|
-
return descriptor.
|
97
|
+
return { start: descriptor.loc, end: null };
|
101
98
|
}
|
102
99
|
|
103
100
|
/**
|
@@ -190,6 +187,8 @@ function normalizeFixes(descriptor, sourceCode) {
|
|
190
187
|
return null;
|
191
188
|
}
|
192
189
|
|
190
|
+
const ruleFixer = new RuleFixer({ sourceCode });
|
191
|
+
|
193
192
|
// @type {null | Fix | Fix[] | IterableIterator<Fix>}
|
194
193
|
const fix = descriptor.fix(ruleFixer);
|
195
194
|
|
@@ -335,6 +334,7 @@ module.exports = function createReportTranslator(metadata) {
|
|
335
334
|
return (...args) => {
|
336
335
|
const descriptor = normalizeMultiArgReportCall(...args);
|
337
336
|
const messages = metadata.messageIds;
|
337
|
+
const { sourceCode } = metadata;
|
338
338
|
|
339
339
|
assertValidNodeInfo(descriptor);
|
340
340
|
|
@@ -367,9 +367,9 @@ module.exports = function createReportTranslator(metadata) {
|
|
367
367
|
node: descriptor.node,
|
368
368
|
message: interpolate(computedMessage, descriptor.data),
|
369
369
|
messageId: descriptor.messageId,
|
370
|
-
loc: normalizeReportLoc(descriptor),
|
371
|
-
fix: metadata.disableFixes ? null : normalizeFixes(descriptor,
|
372
|
-
suggestions: metadata.disableFixes ? [] : mapSuggestions(descriptor,
|
370
|
+
loc: descriptor.loc ? normalizeReportLoc(descriptor) : sourceCode.getLoc(descriptor.node),
|
371
|
+
fix: metadata.disableFixes ? null : normalizeFixes(descriptor, sourceCode),
|
372
|
+
suggestions: metadata.disableFixes ? [] : mapSuggestions(descriptor, sourceCode, messages),
|
373
373
|
language: metadata.language
|
374
374
|
});
|
375
375
|
};
|
package/lib/linter/rule-fixer.js
CHANGED
@@ -4,6 +4,8 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
/* eslint class-methods-use-this: off -- Methods desired on instance */
|
8
|
+
|
7
9
|
//------------------------------------------------------------------------------
|
8
10
|
// Requirements
|
9
11
|
//------------------------------------------------------------------------------
|
@@ -35,8 +37,22 @@ function insertTextAt(index, text) {
|
|
35
37
|
/**
|
36
38
|
* Creates code fixing commands for rules.
|
37
39
|
*/
|
40
|
+
class RuleFixer {
|
38
41
|
|
39
|
-
|
42
|
+
/**
|
43
|
+
* The source code object representing the text to be fixed.
|
44
|
+
* @type {SourceCode}
|
45
|
+
*/
|
46
|
+
#sourceCode;
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Creates a new instance.
|
50
|
+
* @param {Object} options The options for the fixer.
|
51
|
+
* @param {SourceCode} options.sourceCode The source code object representing the text to be fixed.
|
52
|
+
*/
|
53
|
+
constructor({ sourceCode }) {
|
54
|
+
this.#sourceCode = sourceCode;
|
55
|
+
}
|
40
56
|
|
41
57
|
/**
|
42
58
|
* Creates a fix command that inserts text after the given node or token.
|
@@ -46,8 +62,10 @@ const ruleFixer = Object.freeze({
|
|
46
62
|
* @returns {Object} The fix command.
|
47
63
|
*/
|
48
64
|
insertTextAfter(nodeOrToken, text) {
|
49
|
-
|
50
|
-
|
65
|
+
const range = this.#sourceCode.getRange(nodeOrToken);
|
66
|
+
|
67
|
+
return this.insertTextAfterRange(range, text);
|
68
|
+
}
|
51
69
|
|
52
70
|
/**
|
53
71
|
* Creates a fix command that inserts text after the specified range in the source text.
|
@@ -59,7 +77,7 @@ const ruleFixer = Object.freeze({
|
|
59
77
|
*/
|
60
78
|
insertTextAfterRange(range, text) {
|
61
79
|
return insertTextAt(range[1], text);
|
62
|
-
}
|
80
|
+
}
|
63
81
|
|
64
82
|
/**
|
65
83
|
* Creates a fix command that inserts text before the given node or token.
|
@@ -69,8 +87,10 @@ const ruleFixer = Object.freeze({
|
|
69
87
|
* @returns {Object} The fix command.
|
70
88
|
*/
|
71
89
|
insertTextBefore(nodeOrToken, text) {
|
72
|
-
|
73
|
-
|
90
|
+
const range = this.#sourceCode.getRange(nodeOrToken);
|
91
|
+
|
92
|
+
return this.insertTextBeforeRange(range, text);
|
93
|
+
}
|
74
94
|
|
75
95
|
/**
|
76
96
|
* Creates a fix command that inserts text before the specified range in the source text.
|
@@ -82,7 +102,7 @@ const ruleFixer = Object.freeze({
|
|
82
102
|
*/
|
83
103
|
insertTextBeforeRange(range, text) {
|
84
104
|
return insertTextAt(range[0], text);
|
85
|
-
}
|
105
|
+
}
|
86
106
|
|
87
107
|
/**
|
88
108
|
* Creates a fix command that replaces text at the node or token.
|
@@ -92,8 +112,10 @@ const ruleFixer = Object.freeze({
|
|
92
112
|
* @returns {Object} The fix command.
|
93
113
|
*/
|
94
114
|
replaceText(nodeOrToken, text) {
|
95
|
-
|
96
|
-
|
115
|
+
const range = this.#sourceCode.getRange(nodeOrToken);
|
116
|
+
|
117
|
+
return this.replaceTextRange(range, text);
|
118
|
+
}
|
97
119
|
|
98
120
|
/**
|
99
121
|
* Creates a fix command that replaces text at the specified range in the source text.
|
@@ -108,7 +130,7 @@ const ruleFixer = Object.freeze({
|
|
108
130
|
range,
|
109
131
|
text
|
110
132
|
};
|
111
|
-
}
|
133
|
+
}
|
112
134
|
|
113
135
|
/**
|
114
136
|
* Creates a fix command that removes the node or token from the source.
|
@@ -117,8 +139,10 @@ const ruleFixer = Object.freeze({
|
|
117
139
|
* @returns {Object} The fix command.
|
118
140
|
*/
|
119
141
|
remove(nodeOrToken) {
|
120
|
-
|
121
|
-
|
142
|
+
const range = this.#sourceCode.getRange(nodeOrToken);
|
143
|
+
|
144
|
+
return this.removeRange(range);
|
145
|
+
}
|
122
146
|
|
123
147
|
/**
|
124
148
|
* Creates a fix command that removes the specified range of text from the source.
|
@@ -133,8 +157,7 @@ const ruleFixer = Object.freeze({
|
|
133
157
|
text: ""
|
134
158
|
};
|
135
159
|
}
|
136
|
-
|
137
|
-
});
|
160
|
+
}
|
138
161
|
|
139
162
|
|
140
|
-
module.exports =
|
163
|
+
module.exports = { RuleFixer };
|
@@ -42,8 +42,11 @@ module.exports = {
|
|
42
42
|
schema: [],
|
43
43
|
|
44
44
|
messages: {
|
45
|
-
missingAwait: "{{name}} has no 'await' expression."
|
46
|
-
|
45
|
+
missingAwait: "{{name}} has no 'await' expression.",
|
46
|
+
removeAsync: "Remove 'async'."
|
47
|
+
},
|
48
|
+
|
49
|
+
hasSuggestions: true
|
47
50
|
},
|
48
51
|
|
49
52
|
create(context) {
|
@@ -69,6 +72,33 @@ module.exports = {
|
|
69
72
|
*/
|
70
73
|
function exitFunction(node) {
|
71
74
|
if (!node.generator && node.async && !scopeInfo.hasAwait && !astUtils.isEmptyFunction(node)) {
|
75
|
+
|
76
|
+
/*
|
77
|
+
* If the function belongs to a method definition or
|
78
|
+
* property, then the function's range may not include the
|
79
|
+
* `async` keyword and we should look at the parent instead.
|
80
|
+
*/
|
81
|
+
const nodeWithAsyncKeyword =
|
82
|
+
(node.parent.type === "MethodDefinition" && node.parent.value === node) ||
|
83
|
+
(node.parent.type === "Property" && node.parent.method && node.parent.value === node)
|
84
|
+
? node.parent
|
85
|
+
: node;
|
86
|
+
|
87
|
+
const asyncToken = sourceCode.getFirstToken(nodeWithAsyncKeyword, token => token.value === "async");
|
88
|
+
const asyncRange = [asyncToken.range[0], sourceCode.getTokenAfter(asyncToken, { includeComments: true }).range[0]];
|
89
|
+
|
90
|
+
/*
|
91
|
+
* Removing the `async` keyword can cause parsing errors if the current
|
92
|
+
* statement is relying on automatic semicolon insertion. If ASI is currently
|
93
|
+
* being used, then we should replace the `async` keyword with a semicolon.
|
94
|
+
*/
|
95
|
+
const nextToken = sourceCode.getTokenAfter(asyncToken);
|
96
|
+
const addSemiColon =
|
97
|
+
nextToken.type === "Punctuator" &&
|
98
|
+
(nextToken.value === "[" || nextToken.value === "(") &&
|
99
|
+
(nodeWithAsyncKeyword.type === "MethodDefinition" || astUtils.isStartOfExpressionStatement(nodeWithAsyncKeyword)) &&
|
100
|
+
astUtils.needsPrecedingSemicolon(sourceCode, nodeWithAsyncKeyword);
|
101
|
+
|
72
102
|
context.report({
|
73
103
|
node,
|
74
104
|
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
@@ -77,7 +107,11 @@ module.exports = {
|
|
77
107
|
name: capitalizeFirstLetter(
|
78
108
|
astUtils.getFunctionNameWithKind(node)
|
79
109
|
)
|
80
|
-
}
|
110
|
+
},
|
111
|
+
suggest: [{
|
112
|
+
messageId: "removeAsync",
|
113
|
+
fix: fixer => fixer.replaceTextRange(asyncRange, addSemiColon ? ";" : "")
|
114
|
+
}]
|
81
115
|
});
|
82
116
|
}
|
83
117
|
|
@@ -1042,11 +1042,12 @@ function isStartOfExpressionStatement(node) {
|
|
1042
1042
|
|
1043
1043
|
/**
|
1044
1044
|
* Determines whether an opening parenthesis `(`, bracket `[` or backtick ``` ` ``` needs to be preceded by a semicolon.
|
1045
|
-
* This opening parenthesis or bracket should be at the start of an `ExpressionStatement` or at
|
1045
|
+
* This opening parenthesis or bracket should be at the start of an `ExpressionStatement`, a `MethodDefinition` or at
|
1046
|
+
* the start of the body of an `ArrowFunctionExpression`.
|
1046
1047
|
* @type {(sourceCode: SourceCode, node: ASTNode) => boolean}
|
1047
1048
|
* @param {SourceCode} sourceCode The source code object.
|
1048
1049
|
* @param {ASTNode} node A node at the position where an opening parenthesis or bracket will be inserted.
|
1049
|
-
* @returns {boolean} Whether a semicolon is required before the opening parenthesis or
|
1050
|
+
* @returns {boolean} Whether a semicolon is required before the opening parenthesis or bracket.
|
1050
1051
|
*/
|
1051
1052
|
let needsPrecedingSemicolon;
|
1052
1053
|
|
@@ -1106,7 +1107,7 @@ let needsPrecedingSemicolon;
|
|
1106
1107
|
|
1107
1108
|
if (isClosingBraceToken(prevToken)) {
|
1108
1109
|
return (
|
1109
|
-
prevNode.type === "BlockStatement" && prevNode.parent.type === "FunctionExpression" ||
|
1110
|
+
prevNode.type === "BlockStatement" && prevNode.parent.type === "FunctionExpression" && prevNode.parent.parent.type !== "MethodDefinition" ||
|
1110
1111
|
prevNode.type === "ClassBody" && prevNode.parent.type === "ClassExpression" ||
|
1111
1112
|
prevNode.type === "ObjectExpression"
|
1112
1113
|
);
|
package/lib/shared/flags.js
CHANGED
@@ -9,7 +9,8 @@
|
|
9
9
|
* @type {Map<string, string>}
|
10
10
|
*/
|
11
11
|
const activeFlags = new Map([
|
12
|
-
["test_only", "
|
12
|
+
["test_only", "Used only for testing."],
|
13
|
+
["unstable_ts_config", "Enable TypeScript configuration files."]
|
13
14
|
]);
|
14
15
|
|
15
16
|
/**
|
@@ -17,7 +18,7 @@ const activeFlags = new Map([
|
|
17
18
|
* @type {Map<string, string>}
|
18
19
|
*/
|
19
20
|
const inactiveFlags = new Map([
|
20
|
-
["test_only_old", "
|
21
|
+
["test_only_old", "Used only for testing."]
|
21
22
|
]);
|
22
23
|
|
23
24
|
module.exports = {
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "eslint",
|
3
|
-
"version": "9.
|
3
|
+
"version": "9.9.0",
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
6
6
|
"bin": {
|
@@ -68,9 +68,9 @@
|
|
68
68
|
"dependencies": {
|
69
69
|
"@eslint-community/eslint-utils": "^4.2.0",
|
70
70
|
"@eslint-community/regexpp": "^4.11.0",
|
71
|
-
"@eslint/config-array": "^0.17.
|
71
|
+
"@eslint/config-array": "^0.17.1",
|
72
72
|
"@eslint/eslintrc": "^3.1.0",
|
73
|
-
"@eslint/js": "9.
|
73
|
+
"@eslint/js": "9.9.0",
|
74
74
|
"@humanwhocodes/module-importer": "^1.0.1",
|
75
75
|
"@humanwhocodes/retry": "^0.3.0",
|
76
76
|
"@nodelib/fs.walk": "^1.2.8",
|
@@ -104,13 +104,14 @@
|
|
104
104
|
"devDependencies": {
|
105
105
|
"@babel/core": "^7.4.3",
|
106
106
|
"@babel/preset-env": "^7.4.3",
|
107
|
-
"@eslint/core": "^0.
|
107
|
+
"@eslint/core": "^0.3.0",
|
108
|
+
"@eslint/json": "^0.3.0",
|
108
109
|
"@types/estree": "^1.0.5",
|
109
110
|
"@types/node": "^20.11.5",
|
110
|
-
"@wdio/browser-runner": "^8.
|
111
|
-
"@wdio/cli": "^8.
|
112
|
-
"@wdio/concise-reporter": "^8.
|
113
|
-
"@wdio/mocha-framework": "^8.
|
111
|
+
"@wdio/browser-runner": "^8.40.1",
|
112
|
+
"@wdio/cli": "^8.40.0",
|
113
|
+
"@wdio/concise-reporter": "^8.39.0",
|
114
|
+
"@wdio/mocha-framework": "^8.40.0",
|
114
115
|
"babel-loader": "^8.0.5",
|
115
116
|
"c8": "^7.12.0",
|
116
117
|
"chai": "^4.0.1",
|
@@ -131,6 +132,7 @@
|
|
131
132
|
"globals": "^15.0.0",
|
132
133
|
"got": "^11.8.3",
|
133
134
|
"gray-matter": "^4.0.3",
|
135
|
+
"jiti": "^1.21.6",
|
134
136
|
"js-yaml": "^4.1.0",
|
135
137
|
"knip": "^5.21.0",
|
136
138
|
"lint-staged": "^11.0.0",
|
@@ -164,6 +166,14 @@
|
|
164
166
|
"webpack-cli": "^4.5.0",
|
165
167
|
"yorkie": "^2.0.0"
|
166
168
|
},
|
169
|
+
"peerDependencies": {
|
170
|
+
"jiti": "*"
|
171
|
+
},
|
172
|
+
"peerDependenciesMeta": {
|
173
|
+
"jiti": {
|
174
|
+
"optional": true
|
175
|
+
}
|
176
|
+
},
|
167
177
|
"keywords": [
|
168
178
|
"ast",
|
169
179
|
"lint",
|