eslint 10.0.2 → 10.1.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 CHANGED
@@ -43,7 +43,13 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/J
43
43
 
44
44
  ## Installation and Usage
45
45
 
46
- Prerequisites: [Node.js](https://nodejs.org/) (`^20.19.0`, `^22.13.0`, or `>=24`) built with SSL support. (If you are using an official Node.js distribution, SSL is always built in.)
46
+ ### Prerequisites
47
+
48
+ To use ESLint, you must have [Node.js](https://nodejs.org/) (`^20.19.0`, `^22.13.0`, or `>=24`) installed and built with SSL support. (If you are using an official Node.js distribution, SSL is always built in.)
49
+
50
+ If you use ESLint's TypeScript type definitions, TypeScript 5.3 or later is required.
51
+
52
+ ### npm Installation
47
53
 
48
54
  You can install and configure ESLint using this command:
49
55
 
@@ -350,9 +356,9 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
350
356
 
351
357
  <h3>Platinum Sponsors</h3>
352
358
  <p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a></p><h3>Gold Sponsors</h3>
353
- <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://shopify.engineering/"><img src="https://avatars.githubusercontent.com/u/8085" alt="Shopify" height="96"></a></p><h3>Silver Sponsors</h3>
359
+ <p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a></p><h3>Silver Sponsors</h3>
354
360
  <p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/d472863/logo.png" alt="Vite" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/2d6c3b6/logo.png" alt="Liftoff" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
355
- <p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://opensource.sap.com"><img src="https://avatars.githubusercontent.com/u/2531208" alt="SAP" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="https://depot.dev"><img src="https://images.opencollective.com/depot/39125a1/logo.png" alt="Depot" height="32"></a> <a href="https://www.n-ix.com/"><img src="https://images.opencollective.com/n-ix-ltd/575a7a5/logo.png" alt="N-iX Ltd" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="TestMu AI Open Source Office (Formerly LambdaTest)" height="32"></a></p>
361
+ <p><a href="https://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://opensource.sap.com"><img src="https://avatars.githubusercontent.com/u/2531208" alt="SAP" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="https://depot.dev"><img src="https://images.opencollective.com/depot/39125a1/logo.png" alt="Depot" height="32"></a> <a href="https://www.n-ix.com/"><img src="https://images.opencollective.com/n-ix-ltd/575a7a5/logo.png" alt="N-iX Ltd" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="TestMu AI Open Source Office (Formerly LambdaTest)" height="32"></a></p>
356
362
  <h3>Technology Sponsors</h3>
357
363
  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.
358
364
  <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
@@ -395,7 +395,8 @@ const cli = {
395
395
 
396
396
  if (!useStdin) {
397
397
  const suppressionsFileLocation = getCacheFile(
398
- options.suppressionsLocation || "eslint-suppressions.json",
398
+ options.suppressionsLocation ||
399
+ SuppressionsService.DEFAULT_SUPPRESSIONS_FILENAME,
399
400
  process.cwd(),
400
401
  {
401
402
  prefix: "suppressions_",
@@ -19,9 +19,11 @@ const minimatch = require("minimatch");
19
19
  const globParent = require("glob-parent");
20
20
  const { Linter } = require("../linter");
21
21
  const { getShorthandName } = require("../shared/naming");
22
+ const { calculateStatsPerFile } = require("../shared/message-counts");
22
23
  const LintResultCache = require("../cli-engine/lint-result-cache");
23
24
  const { ConfigLoader } = require("../config/config-loader");
24
25
  const createDebug = require("debug");
26
+ const { SuppressionsService } = require("../services/suppressions-service.js");
25
27
 
26
28
  //-----------------------------------------------------------------------------
27
29
  // Fixup references
@@ -719,42 +721,6 @@ function createIgnoreResult(filePath, baseDir, configStatus) {
719
721
  };
720
722
  }
721
723
 
722
- /**
723
- * It will calculate the error and warning count for collection of messages per file
724
- * @param {LintMessage[]} messages Collection of messages
725
- * @returns {Object} Contains the stats
726
- * @private
727
- */
728
- function calculateStatsPerFile(messages) {
729
- const stat = {
730
- errorCount: 0,
731
- fatalErrorCount: 0,
732
- warningCount: 0,
733
- fixableErrorCount: 0,
734
- fixableWarningCount: 0,
735
- };
736
-
737
- for (let i = 0; i < messages.length; i++) {
738
- const message = messages[i];
739
-
740
- if (message.fatal || message.severity === 2) {
741
- stat.errorCount++;
742
- if (message.fatal) {
743
- stat.fatalErrorCount++;
744
- }
745
- if (message.fix) {
746
- stat.fixableErrorCount++;
747
- }
748
- } else {
749
- stat.warningCount++;
750
- if (message.fix) {
751
- stat.fixableWarningCount++;
752
- }
753
- }
754
- }
755
- return stat;
756
- }
757
-
758
724
  //-----------------------------------------------------------------------------
759
725
  // Options-related Helpers
760
726
  //-----------------------------------------------------------------------------
@@ -821,6 +787,8 @@ function processOptions({
821
787
  warnIgnored = true,
822
788
  passOnNoPatterns = false,
823
789
  ruleFilter = () => true,
790
+ applySuppressions = false,
791
+ suppressionsLocation = SuppressionsService.DEFAULT_SUPPRESSIONS_FILENAME,
824
792
  ...unknownOptions
825
793
  }) {
826
794
  const errors = [];
@@ -975,6 +943,12 @@ function processOptions({
975
943
  if (typeof ruleFilter !== "function") {
976
944
  errors.push("'ruleFilter' must be a function.");
977
945
  }
946
+ if (typeof applySuppressions !== "boolean") {
947
+ errors.push("'applySuppressions' must be a boolean.");
948
+ }
949
+ if (!isNonEmptyString(suppressionsLocation)) {
950
+ errors.push("'suppressionsLocation' must be a non-empty string.");
951
+ }
978
952
  if (errors.length > 0) {
979
953
  throw new ESLintInvalidOptionsError(errors);
980
954
  }
@@ -1002,6 +976,8 @@ function processOptions({
1002
976
  passOnNoPatterns,
1003
977
  warnIgnored,
1004
978
  ruleFilter,
979
+ applySuppressions,
980
+ suppressionsLocation,
1005
981
  };
1006
982
  }
1007
983
 
@@ -1444,7 +1420,6 @@ module.exports = {
1444
1420
 
1445
1421
  createIgnoreResult,
1446
1422
  isErrorMessage,
1447
- calculateStatsPerFile,
1448
1423
  getPlaceholderPath,
1449
1424
 
1450
1425
  processOptions,
@@ -46,6 +46,7 @@ const {
46
46
  const { Retrier } = require("@humanwhocodes/retry");
47
47
  const { ConfigLoader } = require("../config/config-loader");
48
48
  const { WarningService } = require("../services/warning-service");
49
+ const { SuppressionsService } = require("../services/suppressions-service");
49
50
  const { Config } = require("../config/config.js");
50
51
  const {
51
52
  getShorthandName,
@@ -68,38 +69,11 @@ const { resolve } = require("../shared/relative-module-resolver.js");
68
69
 
69
70
  /** @typedef {import("../types").Linter.Config} Config */
70
71
  /** @typedef {import("../types").ESLint.DeprecatedRuleUse} DeprecatedRuleInfo */
72
+ /** @typedef {import("../types").ESLint.Options} ESLintOptions */
71
73
  /** @typedef {import("../types").ESLint.LintResult} LintResult */
72
74
  /** @typedef {import("../types").ESLint.Plugin} Plugin */
73
75
  /** @typedef {import("../types").ESLint.ResultsMeta} ResultsMeta */
74
76
 
75
- /**
76
- * The options with which to configure the ESLint instance.
77
- * @typedef {Object} ESLintOptions
78
- * @property {boolean} [allowInlineConfig] Enable or disable inline configuration comments.
79
- * @property {Config|Array<Config>} [baseConfig] Base config, extended by all configs used with this instance
80
- * @property {boolean} [cache] Enable result caching.
81
- * @property {string} [cacheLocation] The cache file to use instead of .eslintcache.
82
- * @property {"metadata" | "content"} [cacheStrategy] The strategy used to detect changed files.
83
- * @property {number | "auto" | "off"} [concurrency] Maximum number of linting threads, "auto" to choose automatically, "off" for no multithreading.
84
- * @property {string} [cwd] The value to use for the current working directory.
85
- * @property {boolean} [errorOnUnmatchedPattern] If `false` then `ESLint#lintFiles()` doesn't throw even if no target files found. Defaults to `true`.
86
- * @property {boolean|Function} [fix] Execute in autofix mode. If a function, should return a boolean.
87
- * @property {string[]} [fixTypes] Array of rule types to apply fixes for.
88
- * @property {string[]} [flags] Array of feature flags to enable.
89
- * @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.
90
- * @property {boolean} [ignore] False disables all ignore patterns except for the default ones.
91
- * @property {string[]} [ignorePatterns] Ignore file patterns to use in addition to config ignores. These patterns are relative to `cwd`.
92
- * @property {Config|Array<Config>} [overrideConfig] Override config, overrides all configs used with this instance
93
- * @property {boolean|string} [overrideConfigFile] Searches for default config file when falsy;
94
- * doesn't do any config file lookup when `true`; considered to be a config filename
95
- * when a string.
96
- * @property {boolean} [passOnNoPatterns=false] When set to true, missing patterns cause
97
- * the linting operation to short circuit and not report any failures.
98
- * @property {Record<string,Plugin>} [plugins] An array of plugin implementations.
99
- * @property {boolean} [stats] True enables added statistics on lint results.
100
- * @property {boolean} [warnIgnored] Show warnings when the file list includes ignored files
101
- */
102
-
103
77
  //------------------------------------------------------------------------------
104
78
  // Helpers
105
79
  //------------------------------------------------------------------------------
@@ -237,7 +211,7 @@ function compareResultsByFilePath(a, b) {
237
211
  *
238
212
  * This function is used primarily by the `--inspect-config` option. For now,
239
213
  * we will maintain the existing behavior, which is to search up from the cwd.
240
- * @param {ESLintOptions} options The ESLint instance options.
214
+ * @param {{ configFile: string | undefined; cwd: string; }} options Config file location options.
241
215
  * @returns {Promise<{configFilePath:string|undefined;basePath:string}>} Location information for
242
216
  * the config file.
243
217
  */
@@ -739,6 +713,20 @@ class ESLint {
739
713
  processedOptions,
740
714
  cacheFilePath,
741
715
  );
716
+
717
+ let suppressionsService = null;
718
+ if (processedOptions.applySuppressions) {
719
+ const suppressionsFilePath = getCacheFile(
720
+ processedOptions.suppressionsLocation,
721
+ processedOptions.cwd,
722
+ { prefix: "suppressions_" },
723
+ );
724
+ suppressionsService = new SuppressionsService({
725
+ filePath: suppressionsFilePath,
726
+ cwd: processedOptions.cwd,
727
+ });
728
+ }
729
+
742
730
  const defaultConfigs = createDefaultConfigs(options.plugins);
743
731
 
744
732
  this.#configLoader = createConfigLoader(
@@ -759,6 +747,7 @@ class ESLint {
759
747
  configs: null,
760
748
  configLoader: this.#configLoader,
761
749
  warningService,
750
+ suppressionsService,
762
751
  });
763
752
 
764
753
  // Check for the .eslintignore file, and warn if it's present.
@@ -953,6 +942,7 @@ class ESLint {
953
942
  lintResultCache,
954
943
  options: eslintOptions,
955
944
  warningService,
945
+ suppressionsService,
956
946
  } = privateMembers.get(this);
957
947
 
958
948
  /*
@@ -1065,9 +1055,19 @@ class ESLint {
1065
1055
  lintResultCache.reconcile();
1066
1056
  }
1067
1057
 
1068
- const finalResults = results.filter(result => !!result);
1058
+ const unsuppressedResults = results.filter(result => !!result);
1059
+
1060
+ if (!eslintOptions.applySuppressions) {
1061
+ return processLintReport(this, unsuppressedResults);
1062
+ }
1063
+
1064
+ const { results: suppressedResults } =
1065
+ suppressionsService.applySuppressions(
1066
+ unsuppressedResults,
1067
+ await suppressionsService.load(),
1068
+ );
1069
1069
 
1070
- return processLintReport(this, finalResults);
1070
+ return processLintReport(this, suppressedResults);
1071
1071
  }
1072
1072
 
1073
1073
  /**
@@ -1118,7 +1118,11 @@ class ESLint {
1118
1118
 
1119
1119
  // Now we can get down to linting
1120
1120
 
1121
- const { linter, options: eslintOptions } = privateMembers.get(this);
1121
+ const {
1122
+ linter,
1123
+ options: eslintOptions,
1124
+ suppressionsService,
1125
+ } = privateMembers.get(this);
1122
1126
  const {
1123
1127
  allowInlineConfig,
1124
1128
  cwd,
@@ -1176,7 +1180,16 @@ class ESLint {
1176
1180
 
1177
1181
  debug("Linting complete in %t", hrtimeBigint() - startTime);
1178
1182
 
1179
- return processLintReport(this, results);
1183
+ if (!filePath || !eslintOptions.applySuppressions) {
1184
+ return processLintReport(this, results);
1185
+ }
1186
+
1187
+ const { results: suppressedResults } =
1188
+ suppressionsService.applySuppressions(
1189
+ results,
1190
+ await suppressionsService.load(),
1191
+ );
1192
+ return processLintReport(this, suppressedResults);
1180
1193
  }
1181
1194
 
1182
1195
  /**
@@ -19,7 +19,6 @@ function search(tokens, location) {
19
19
  for (
20
20
  let minIndex = 0, maxIndex = tokens.length - 1;
21
21
  minIndex <= maxIndex;
22
-
23
22
  ) {
24
23
  /*
25
24
  * Calculate the index in the middle between minIndex and maxIndex.
@@ -176,9 +176,9 @@ module.exports = {
176
176
 
177
177
  return Boolean(
178
178
  previousToken &&
179
- nextToken &&
180
- comment.loc.start.line === previousToken.loc.end.line &&
181
- comment.loc.end.line === nextToken.loc.start.line,
179
+ nextToken &&
180
+ comment.loc.start.line === previousToken.loc.end.line &&
181
+ comment.loc.end.line === nextToken.loc.start.line,
182
182
  );
183
183
  }
184
184
 
@@ -194,7 +194,7 @@ module.exports = {
194
194
 
195
195
  return Boolean(
196
196
  previousTokenOrComment &&
197
- ["Block", "Line"].includes(previousTokenOrComment.type),
197
+ ["Block", "Line"].includes(previousTokenOrComment.type),
198
198
  );
199
199
  }
200
200
 
@@ -87,11 +87,11 @@ module.exports = {
87
87
  function isPrototypePropertyAccessed(identifierNode) {
88
88
  return Boolean(
89
89
  identifierNode &&
90
- identifierNode.parent &&
91
- identifierNode.parent.type === "MemberExpression" &&
92
- identifierNode.parent.object === identifierNode &&
93
- astUtils.getStaticPropertyName(identifierNode.parent) ===
94
- "prototype",
90
+ identifierNode.parent &&
91
+ identifierNode.parent.type === "MemberExpression" &&
92
+ identifierNode.parent.object === identifierNode &&
93
+ astUtils.getStaticPropertyName(identifierNode.parent) ===
94
+ "prototype",
95
95
  );
96
96
  }
97
97
 
@@ -181,9 +181,7 @@ module.exports = {
181
181
  isConstructor: true,
182
182
  hasExtends: Boolean(
183
183
  classNode.superClass &&
184
- !astUtils.isNullOrUndefined(
185
- classNode.superClass,
186
- ),
184
+ !astUtils.isNullOrUndefined(classNode.superClass),
187
185
  ),
188
186
  codePath,
189
187
  currentSegments: new Set(),
@@ -152,11 +152,11 @@ function updateModifiedFlag(conditions, modifiers) {
152
152
  condition.isInLoop(modifier) ||
153
153
  Boolean(
154
154
  (funcNode = getEncloseFunctionDeclaration(modifier)) &&
155
- (funcVar = astUtils.getVariableByName(
156
- modifier.from.upper,
157
- funcNode.id.name,
158
- )) &&
159
- funcVar.references.some(condition.isInLoop),
155
+ (funcVar = astUtils.getVariableByName(
156
+ modifier.from.upper,
157
+ funcNode.id.name,
158
+ )) &&
159
+ funcVar.references.some(condition.isInLoop),
160
160
  );
161
161
 
162
162
  condition.modified = inLoop;
@@ -138,7 +138,7 @@ module.exports = {
138
138
 
139
139
  messages: {
140
140
  unnecessaryAssignment:
141
- "This assigned value is not used in subsequent statements.",
141
+ "The value assigned to '{{name}}' is not used in subsequent statements.",
142
142
  },
143
143
  },
144
144
 
@@ -486,6 +486,7 @@ module.exports = {
486
486
  context.report({
487
487
  node: targetAssignment.identifier,
488
488
  messageId: "unnecessaryAssignment",
489
+ data: { name: targetAssignment.identifier.name },
489
490
  });
490
491
  }
491
492
 
@@ -199,6 +199,28 @@ function hasNameDisallowedForLetDeclarations(variable) {
199
199
  return variable.name === "let";
200
200
  }
201
201
 
202
+ /**
203
+ * Checks whether a given variable has any references before its declaration.
204
+ * This is important because var allows hoisting, but let/const do not.
205
+ * @param {Variable} variable The variable to check.
206
+ * @returns {boolean} `true` if the variable is referenced before its declaration.
207
+ */
208
+ function hasReferenceBeforeDeclaration(variable) {
209
+ const declarationStart = variable.defs[0].node.range[0];
210
+
211
+ return variable.references.some(reference => {
212
+ const referenceStart = reference.identifier.range[0];
213
+
214
+ /*
215
+ * Check if the reference occurs before the declaration.
216
+ * We don't need to check scopes because all references to this variable
217
+ * are already in variable.references (which only includes references
218
+ * that resolve to this specific variable binding).
219
+ */
220
+ return !reference.init && referenceStart < declarationStart;
221
+ });
222
+ }
223
+
202
224
  //------------------------------------------------------------------------------
203
225
  // Rule Definition
204
226
  //------------------------------------------------------------------------------
@@ -252,6 +274,7 @@ module.exports = {
252
274
  * - A variable is used from a closure within a loop.
253
275
  * - A variable might be used before it is assigned within a loop.
254
276
  * - A variable might be used in TDZ.
277
+ * - A variable is referenced before its declaration.
255
278
  * - A variable is declared in statement position (e.g. a single-line `IfStatement`)
256
279
  * - A variable has name that is disallowed for `let` declarations.
257
280
  *
@@ -296,6 +319,10 @@ module.exports = {
296
319
  function canFix(node) {
297
320
  const variables = sourceCode.getDeclaredVariables(node);
298
321
  const scopeNode = getScopeNode(node);
322
+ const parentStatementList = new Set([
323
+ ...astUtils.STATEMENT_LIST_PARENTS,
324
+ "TSModuleBlock",
325
+ ]);
299
326
 
300
327
  if (
301
328
  node.parent.type === "SwitchCase" ||
@@ -303,7 +330,8 @@ module.exports = {
303
330
  variables.some(isGlobal) ||
304
331
  variables.some(isRedeclared) ||
305
332
  variables.some(isUsedFromOutsideOf(scopeNode)) ||
306
- variables.some(hasNameDisallowedForLetDeclarations)
333
+ variables.some(hasNameDisallowedForLetDeclarations) ||
334
+ variables.some(hasReferenceBeforeDeclaration)
307
335
  ) {
308
336
  return false;
309
337
  }
@@ -323,7 +351,7 @@ module.exports = {
323
351
  node.parent.type === "ForStatement" &&
324
352
  node.parent.init === node
325
353
  ) &&
326
- !astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)
354
+ !parentStatementList.has(node.parent.type)
327
355
  ) {
328
356
  // If the declaration is not in a block, e.g. `if (foo) var bar = 1;`, then it can't be fixed.
329
357
  return false;
@@ -158,11 +158,11 @@ function getActualLastToken(sourceCode, node) {
158
158
  const nextToken = sourceCode.getTokenAfter(semiToken);
159
159
  const isSemicolonLessStyle = Boolean(
160
160
  prevToken &&
161
- nextToken &&
162
- prevToken.range[0] >= node.range[0] &&
163
- astUtils.isSemicolonToken(semiToken) &&
164
- semiToken.loc.start.line !== prevToken.loc.end.line &&
165
- semiToken.loc.end.line === nextToken.loc.start.line,
161
+ nextToken &&
162
+ prevToken.range[0] >= node.range[0] &&
163
+ astUtils.isSemicolonToken(semiToken) &&
164
+ semiToken.loc.start.line !== prevToken.loc.end.line &&
165
+ semiToken.loc.end.line === nextToken.loc.start.line,
166
166
  );
167
167
 
168
168
  return isSemicolonLessStyle ? prevToken : semiToken;
@@ -410,9 +410,9 @@ function equalLiteralValue(left, right) {
410
410
  if (left.regex || right.regex) {
411
411
  return Boolean(
412
412
  left.regex &&
413
- right.regex &&
414
- left.regex.pattern === right.regex.pattern &&
415
- left.regex.flags === right.regex.flags,
413
+ right.regex &&
414
+ left.regex.pattern === right.regex.pattern &&
415
+ left.regex.flags === right.regex.flags,
416
416
  );
417
417
  }
418
418
 
@@ -1053,9 +1053,9 @@ function isReferenceToGlobalVariable(scope, node) {
1053
1053
 
1054
1054
  return Boolean(
1055
1055
  reference &&
1056
- reference.resolved &&
1057
- reference.resolved.scope.type === "global" &&
1058
- reference.resolved.defs.length === 0,
1056
+ reference.resolved &&
1057
+ reference.resolved.scope.type === "global" &&
1058
+ reference.resolved.defs.length === 0,
1059
1059
  );
1060
1060
  }
1061
1061
 
@@ -6,8 +6,8 @@
6
6
  /**
7
7
  * Check whether a given character is a combining mark or not.
8
8
  * @param {number} codePoint The character code to check.
9
- * @returns {boolean} `true` if the character belongs to the category, any of `Mc`, `Me`, and `Mn`.
9
+ * @returns {boolean} `true` if the character has the General Category of Combining Mark (M), consisting of `Mc`, `Me`, and `Mn`.
10
10
  */
11
11
  module.exports = function isCombiningCharacter(codePoint) {
12
- return /^[\p{Mc}\p{Me}\p{Mn}]$/u.test(String.fromCodePoint(codePoint));
12
+ return /^\p{M}$/u.test(String.fromCodePoint(codePoint));
13
13
  };
@@ -11,7 +11,7 @@
11
11
 
12
12
  const fs = require("node:fs");
13
13
  const path = require("node:path");
14
- const { calculateStatsPerFile } = require("../eslint/eslint-helpers");
14
+ const { calculateStatsPerFile } = require("../shared/message-counts");
15
15
  const stringify = require("json-stable-stringify-without-jsonify");
16
16
 
17
17
  //------------------------------------------------------------------------------
@@ -31,6 +31,8 @@ const stringify = require("json-stable-stringify-without-jsonify");
31
31
  * Manages the suppressed violations.
32
32
  */
33
33
  class SuppressionsService {
34
+ static DEFAULT_SUPPRESSIONS_FILENAME = "eslint-suppressions.json";
35
+
34
36
  filePath = "";
35
37
  cwd = "";
36
38
 
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @fileoverview Helpers for counting errors and warnings in lint messages.
3
+ * @author Nicholas C. Zakas
4
+ * @author Blake Sager
5
+ */
6
+
7
+ "use strict";
8
+
9
+ /**
10
+ * It will calculate the error and warning count for collection of messages per file
11
+ * @param {LintMessage[]} messages Collection of messages
12
+ * @returns {Object} Contains the stats
13
+ */
14
+ function calculateStatsPerFile(messages) {
15
+ const stat = {
16
+ errorCount: 0,
17
+ fatalErrorCount: 0,
18
+ warningCount: 0,
19
+ fixableErrorCount: 0,
20
+ fixableWarningCount: 0,
21
+ };
22
+
23
+ for (let i = 0; i < messages.length; i++) {
24
+ const message = messages[i];
25
+
26
+ if (message.fatal || message.severity === 2) {
27
+ stat.errorCount++;
28
+ if (message.fatal) {
29
+ stat.fatalErrorCount++;
30
+ }
31
+ if (message.fix) {
32
+ stat.fixableErrorCount++;
33
+ }
34
+ } else {
35
+ stat.warningCount++;
36
+ if (message.fix) {
37
+ stat.fixableWarningCount++;
38
+ }
39
+ }
40
+ }
41
+ return stat;
42
+ }
43
+
44
+ module.exports = {
45
+ calculateStatsPerFile,
46
+ };
@@ -237,15 +237,12 @@ export namespace Scope {
237
237
 
238
238
  // #region SourceCode
239
239
 
240
- export class SourceCode
241
- implements
242
- TextSourceCode<{
243
- LangOptions: Linter.LanguageOptions;
244
- RootNode: AST.Program;
245
- SyntaxElementWithLoc: AST.Token | ESTree.Node;
246
- ConfigNode: ESTree.Comment;
247
- }>
248
- {
240
+ export class SourceCode implements TextSourceCode<{
241
+ LangOptions: Linter.LanguageOptions;
242
+ RootNode: AST.Program;
243
+ SyntaxElementWithLoc: AST.Token | ESTree.Node;
244
+ ConfigNode: ESTree.Comment;
245
+ }> {
249
246
  text: string;
250
247
  ast: AST.Program;
251
248
  lines: string[];
@@ -649,32 +646,30 @@ export type JSSyntaxElement = {
649
646
  };
650
647
 
651
648
  export namespace Rule {
652
- interface RuleModule
653
- extends RuleDefinition<{
654
- LangOptions: Linter.LanguageOptions;
655
- Code: SourceCode;
656
- RuleOptions: any[];
657
- Visitor: RuleListener;
658
- Node: JSSyntaxElement;
659
- MessageIds: string;
660
- ExtRuleDocs: {};
661
- }> {
649
+ interface RuleModule extends RuleDefinition<{
650
+ LangOptions: Linter.LanguageOptions;
651
+ Code: SourceCode;
652
+ RuleOptions: any[];
653
+ Visitor: RuleListener;
654
+ Node: JSSyntaxElement;
655
+ MessageIds: string;
656
+ ExtRuleDocs: {};
657
+ }> {
662
658
  create(context: RuleContext): RuleListener;
663
659
  }
664
660
 
665
661
  type NodeTypes = ESTree.Node["type"];
666
662
 
667
- interface NodeListener
668
- extends CustomRuleVisitorWithExit<
669
- {
670
- [Node in Rule.Node as Node["type"]]?:
671
- | ((node: Node) => void)
672
- | undefined;
673
- } & {
674
- // A `Program` visitor's node type has no `parent` property.
675
- Program?: ((node: AST.Program) => void) | undefined;
676
- }
677
- > {}
663
+ interface NodeListener extends CustomRuleVisitorWithExit<
664
+ {
665
+ [Node in Rule.Node as Node["type"]]?:
666
+ | ((node: Node) => void)
667
+ | undefined;
668
+ } & {
669
+ // A `Program` visitor's node type has no `parent` property.
670
+ Program?: ((node: AST.Program) => void) | undefined;
671
+ }
672
+ > {}
678
673
 
679
674
  interface NodeParentExtension {
680
675
  parent: Node;
@@ -748,14 +743,13 @@ export namespace Rule {
748
743
 
749
744
  type RuleMetaData = RulesMeta;
750
745
 
751
- interface RuleContext
752
- extends CoreRuleContext<{
753
- LangOptions: Linter.LanguageOptions;
754
- Code: SourceCode;
755
- RuleOptions: any[];
756
- Node: JSSyntaxElement;
757
- MessageIds: string;
758
- }> {}
746
+ interface RuleContext extends CoreRuleContext<{
747
+ LangOptions: Linter.LanguageOptions;
748
+ Code: SourceCode;
749
+ RuleOptions: any[];
750
+ Node: JSSyntaxElement;
751
+ MessageIds: string;
752
+ }> {}
759
753
 
760
754
  type ReportFixer = CoreRuleFixer;
761
755
 
@@ -1195,41 +1189,98 @@ export namespace ESLint {
1195
1189
 
1196
1190
  type CacheStrategy = "content" | "metadata";
1197
1191
 
1192
+ /** The options with which to configure the ESLint instance. */
1198
1193
  interface Options {
1199
1194
  // File enumeration
1195
+
1196
+ /** The value to use for the current working directory. */
1200
1197
  cwd?: string | undefined;
1198
+
1199
+ /** If `false` then `ESLint#lintFiles()` doesn't throw even if no target files found. Defaults to `true`. */
1201
1200
  errorOnUnmatchedPattern?: boolean | undefined;
1201
+
1202
+ /**
1203
+ * Set to false to skip glob resolution of input file paths to lint (default: true).
1204
+ * If false, each input file path is assumed to be a non-glob path to an existing file.
1205
+ */
1202
1206
  globInputPaths?: boolean | undefined;
1207
+
1208
+ /** False disables all ignore patterns except for the default ones. */
1203
1209
  ignore?: boolean | undefined;
1210
+
1211
+ /** Ignore file patterns to use in addition to config ignores. These patterns are relative to `cwd`. */
1204
1212
  ignorePatterns?: string[] | null | undefined;
1213
+
1214
+ /** When set to true, missing patterns cause the linting operation to short circuit and not report any failures. */
1205
1215
  passOnNoPatterns?: boolean | undefined;
1216
+
1217
+ /** Show warnings when the file list includes ignored files. */
1206
1218
  warnIgnored?: boolean | undefined;
1207
1219
 
1208
1220
  // Linting
1221
+
1222
+ /** Enable or disable inline configuration comments. */
1209
1223
  allowInlineConfig?: boolean | undefined;
1224
+
1225
+ /** Base config, extended by all configs used with this instance. */
1210
1226
  baseConfig?: Linter.Config | Linter.Config[] | null | undefined;
1227
+
1228
+ /** Override config, overrides all configs used with this instance. */
1211
1229
  overrideConfig?: Linter.Config | Linter.Config[] | null | undefined;
1230
+
1231
+ /**
1232
+ * Searches for default config file when falsy; doesn't do any config file lookup when `true`; considered to be a config filename when a string.
1233
+ */
1212
1234
  overrideConfigFile?: string | true | null | undefined;
1235
+
1236
+ /** An array of plugin implementations. */
1213
1237
  plugins?: Record<string, Plugin> | null | undefined;
1238
+
1239
+ /**
1240
+ * Default is `() => true`. A predicate function that filters rules to be run.
1241
+ * This function is called with an object containing `ruleId` and `severity`, and returns `true` if the rule should be run.
1242
+ */
1214
1243
  ruleFilter?:
1215
1244
  | ((arg: {
1216
1245
  ruleId: string;
1217
1246
  severity: Exclude<Linter.Severity, 0>;
1218
1247
  }) => boolean)
1219
1248
  | undefined;
1249
+
1250
+ /** True enables added statistics on lint results. */
1220
1251
  stats?: boolean | undefined;
1221
1252
 
1222
1253
  // Autofix
1254
+
1255
+ /** Execute in autofix mode. If a function, should return a boolean. */
1223
1256
  fix?: boolean | ((message: Linter.LintMessage) => boolean) | undefined;
1257
+
1258
+ /** Array of rule types to apply fixes for. */
1224
1259
  fixTypes?: FixType[] | null | undefined;
1225
1260
 
1226
1261
  // Cache-related
1262
+
1263
+ /** Enable result caching. */
1227
1264
  cache?: boolean | undefined;
1265
+
1266
+ /** The cache file to use instead of .eslintcache. */
1228
1267
  cacheLocation?: string | undefined;
1268
+
1269
+ /** The strategy used to detect changed files. */
1229
1270
  cacheStrategy?: CacheStrategy | undefined;
1230
1271
 
1272
+ /** If true, apply suppressions automatically. Defaults to false. */
1273
+ applySuppressions?: boolean | undefined;
1274
+
1275
+ /** Path to suppressions file. Relative to cwd. Defaults to eslint-suppressions.json in cwd. */
1276
+ suppressionsLocation?: string | undefined;
1277
+
1231
1278
  // Other Options
1279
+
1280
+ /** Maximum number of linting threads, "auto" to choose automatically, "off" for no multithreading. */
1232
1281
  concurrency?: number | "auto" | "off" | undefined;
1282
+
1283
+ /** Array of feature flags to enable. */
1233
1284
  flags?: string[] | undefined;
1234
1285
  }
1235
1286
 
@@ -1428,17 +1479,16 @@ export class RuleTester {
1428
1479
  }
1429
1480
 
1430
1481
  export namespace RuleTester {
1431
- interface ValidTestCase
1432
- extends Omit<
1433
- Linter.Config,
1434
- | "name"
1435
- | "basePath"
1436
- | "files"
1437
- | "ignores"
1438
- | "linterOptions"
1439
- | "plugins"
1440
- | "rules"
1441
- > {
1482
+ interface ValidTestCase extends Omit<
1483
+ Linter.Config,
1484
+ | "name"
1485
+ | "basePath"
1486
+ | "files"
1487
+ | "ignores"
1488
+ | "linterOptions"
1489
+ | "plugins"
1490
+ | "rules"
1491
+ > {
1442
1492
  name?: string;
1443
1493
  code: string;
1444
1494
  options?: any[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "10.0.2",
3
+ "version": "10.1.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "type": "commonjs",
@@ -53,12 +53,14 @@
53
53
  "build:webpack": "node Makefile.js webpack",
54
54
  "build:readme": "node tools/update-readme.js",
55
55
  "build:rules-index": "node Makefile.js generateRuleIndexPage",
56
- "lint": "trunk check --no-fix --ignore=docs/**/*.js -a --filter=eslint && trunk check --no-fix --ignore=docs/**/*.js",
57
- "lint:docs:js": "trunk check --no-fix --ignore=** --ignore=!docs/**/*.js -a --filter=eslint && trunk check --no-fix --ignore=** --ignore=!docs/**/*.js",
56
+ "fmt": "prettier --write .",
57
+ "fmt:check": "prettier --check .",
58
+ "lint": "node Makefile.js lint",
59
+ "lint:docs:js": "node Makefile.js lintDocsJS",
58
60
  "lint:docs:rule-examples": "node Makefile.js checkRuleExamples",
59
61
  "lint:unused": "knip",
60
- "lint:fix": "trunk check -y --ignore=docs/**/*.js -a --filter=eslint && trunk check -y --ignore=docs/**/*.js",
61
- "lint:fix:docs:js": "trunk check -y --ignore=** --ignore=!docs/**/*.js -a --filter=eslint && trunk check -y --ignore=** --ignore=!docs/**/*.js",
62
+ "lint:fix": "node Makefile.js lint -- fix",
63
+ "lint:fix:docs:js": "node Makefile.js lintDocsJS -- fix",
62
64
  "lint:rule-types": "node tools/update-rule-type-headers.js --check",
63
65
  "lint:types": "attw --pack",
64
66
  "release:generate:alpha": "node Makefile.js generatePrerelease -- alpha",
@@ -80,7 +82,15 @@
80
82
  "pre-commit": "lint-staged"
81
83
  },
82
84
  "lint-staged": {
83
- "*": "trunk check --fix",
85
+ "*.{js,json,jsonc,json5,yml,yaml}": [
86
+ "eslint --fix",
87
+ "prettier --write"
88
+ ],
89
+ "docs/src/**/*.md": [
90
+ "markdownlint-cli2 --fix",
91
+ "prettier --write"
92
+ ],
93
+ "!({{*.,**/*.}{js,json,jsonc,json5,yml,yaml},docs/src/**/*.md})": "prettier --write --ignore-unknown",
84
94
  "lib/rules/*.js": [
85
95
  "node tools/update-eslint-all.js",
86
96
  "node tools/update-eslint-recommended.js",
@@ -91,7 +101,8 @@
91
101
  "node tools/check-rule-examples.js",
92
102
  "node tools/fetch-docs-links.js",
93
103
  "git add docs/src/_data/further_reading_links.json"
94
- ]
104
+ ],
105
+ "docs/**/*.svg": "npx -y svgo -r --multipass"
95
106
  },
96
107
  "files": [
97
108
  "LICENSE",
@@ -108,10 +119,10 @@
108
119
  "dependencies": {
109
120
  "@eslint-community/eslint-utils": "^4.8.0",
110
121
  "@eslint-community/regexpp": "^4.12.2",
111
- "@eslint/config-array": "^0.23.2",
112
- "@eslint/config-helpers": "^0.5.2",
113
- "@eslint/core": "^1.1.0",
114
- "@eslint/plugin-kit": "^0.6.0",
122
+ "@eslint/config-array": "^0.23.3",
123
+ "@eslint/config-helpers": "^0.5.3",
124
+ "@eslint/core": "^1.1.1",
125
+ "@eslint/plugin-kit": "^0.6.1",
115
126
  "@humanfs/node": "^0.16.6",
116
127
  "@humanwhocodes/module-importer": "^1.0.1",
117
128
  "@humanwhocodes/retry": "^0.4.2",
@@ -120,9 +131,9 @@
120
131
  "cross-spawn": "^7.0.6",
121
132
  "debug": "^4.3.2",
122
133
  "escape-string-regexp": "^4.0.0",
123
- "eslint-scope": "^9.1.1",
134
+ "eslint-scope": "^9.1.2",
124
135
  "eslint-visitor-keys": "^5.0.1",
125
- "espree": "^11.1.1",
136
+ "espree": "^11.2.0",
126
137
  "esquery": "^1.7.0",
127
138
  "esutils": "^2.0.2",
128
139
  "fast-deep-equal": "^3.1.3",
@@ -133,7 +144,7 @@
133
144
  "imurmurhash": "^0.1.4",
134
145
  "is-glob": "^4.0.0",
135
146
  "json-stable-stringify-without-jsonify": "^1.0.1",
136
- "minimatch": "^10.2.1",
147
+ "minimatch": "^10.2.4",
137
148
  "natural-compare": "^1.4.0",
138
149
  "optionator": "^0.9.3"
139
150
  },
@@ -142,14 +153,13 @@
142
153
  "@babel/core": "^7.4.3",
143
154
  "@babel/preset-env": "^7.4.3",
144
155
  "@cypress/webpack-preprocessor": "^6.0.2",
145
- "@eslint/json": "^0.14.0",
146
- "@eslint/eslintrc": "^3.3.4",
147
- "@trunkio/launcher": "^1.3.4",
156
+ "@eslint/eslintrc": "^3.3.5",
157
+ "@eslint/json": "^1.2.0",
148
158
  "@types/esquery": "^1.5.4",
149
159
  "@types/node": "^22.13.14",
150
160
  "@typescript-eslint/parser": "^8.56.0",
151
161
  "babel-loader": "^8.0.5",
152
- "c8": "^7.12.0",
162
+ "c8": "^11.0.0",
153
163
  "chai": "^4.0.1",
154
164
  "cheerio": "^0.22.0",
155
165
  "common-tags": "^1.8.0",
@@ -175,6 +185,7 @@
175
185
  "lint-staged": "^11.0.0",
176
186
  "markdown-it": "^12.2.0",
177
187
  "markdown-it-container": "^3.0.0",
188
+ "markdownlint-cli2": "^0.21.0",
178
189
  "marked": "^4.0.8",
179
190
  "metascraper": "^5.25.7",
180
191
  "metascraper-description": "^5.25.7",
@@ -185,7 +196,7 @@
185
196
  "mocha": "^11.7.1",
186
197
  "node-polyfill-webpack-plugin": "^1.0.3",
187
198
  "npm-license": "^0.3.3",
188
- "prettier": "3.5.3",
199
+ "prettier": "3.8.1",
189
200
  "progress": "^2.0.3",
190
201
  "proxyquire": "^2.0.1",
191
202
  "recast": "^0.23.0",