eslint 9.0.0-alpha.0 → 9.0.0-alpha.1

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.
Files changed (85) hide show
  1. package/README.md +5 -0
  2. package/conf/ecma-version.js +16 -0
  3. package/lib/cli-engine/cli-engine.js +1 -1
  4. package/lib/cli-engine/lint-result-cache.js +2 -2
  5. package/lib/cli.js +14 -16
  6. package/lib/linter/apply-disable-directives.js +2 -2
  7. package/lib/linter/code-path-analysis/code-path.js +5 -19
  8. package/lib/linter/code-path-analysis/fork-context.js +1 -1
  9. package/lib/linter/config-comment-parser.js +7 -10
  10. package/lib/linter/linter.js +105 -4
  11. package/lib/linter/report-translator.js +2 -2
  12. package/lib/linter/source-code-fixer.js +1 -1
  13. package/lib/rule-tester/rule-tester.js +1 -26
  14. package/lib/rules/array-bracket-newline.js +1 -1
  15. package/lib/rules/array-bracket-spacing.js +1 -1
  16. package/lib/rules/block-scoped-var.js +1 -1
  17. package/lib/rules/callback-return.js +2 -2
  18. package/lib/rules/comma-dangle.js +1 -1
  19. package/lib/rules/comma-style.js +2 -2
  20. package/lib/rules/complexity.js +1 -1
  21. package/lib/rules/constructor-super.js +1 -1
  22. package/lib/rules/default-case.js +1 -1
  23. package/lib/rules/eol-last.js +2 -2
  24. package/lib/rules/function-paren-newline.js +2 -2
  25. package/lib/rules/indent-legacy.js +5 -5
  26. package/lib/rules/indent.js +5 -5
  27. package/lib/rules/index.js +1 -0
  28. package/lib/rules/key-spacing.js +2 -2
  29. package/lib/rules/line-comment-position.js +1 -1
  30. package/lib/rules/lines-around-directive.js +2 -2
  31. package/lib/rules/max-depth.js +1 -1
  32. package/lib/rules/max-len.js +3 -3
  33. package/lib/rules/max-lines.js +3 -3
  34. package/lib/rules/max-nested-callbacks.js +1 -1
  35. package/lib/rules/max-params.js +1 -1
  36. package/lib/rules/max-statements.js +1 -1
  37. package/lib/rules/multiline-comment-style.js +7 -7
  38. package/lib/rules/new-cap.js +1 -1
  39. package/lib/rules/newline-after-var.js +1 -1
  40. package/lib/rules/newline-before-return.js +1 -1
  41. package/lib/rules/no-constant-binary-expression.js +5 -5
  42. package/lib/rules/no-constructor-return.js +1 -1
  43. package/lib/rules/no-dupe-class-members.js +2 -2
  44. package/lib/rules/no-else-return.js +1 -1
  45. package/lib/rules/no-empty-function.js +2 -2
  46. package/lib/rules/no-fallthrough.js +1 -1
  47. package/lib/rules/no-inner-declarations.js +22 -1
  48. package/lib/rules/no-invalid-this.js +1 -1
  49. package/lib/rules/no-lone-blocks.js +2 -2
  50. package/lib/rules/no-loss-of-precision.js +1 -1
  51. package/lib/rules/no-misleading-character-class.js +174 -65
  52. package/lib/rules/no-multiple-empty-lines.js +1 -1
  53. package/lib/rules/no-restricted-globals.js +1 -1
  54. package/lib/rules/no-restricted-imports.js +2 -2
  55. package/lib/rules/no-restricted-modules.js +2 -2
  56. package/lib/rules/no-return-await.js +1 -1
  57. package/lib/rules/no-trailing-spaces.js +2 -3
  58. package/lib/rules/no-unneeded-ternary.js +1 -1
  59. package/lib/rules/no-unsafe-optional-chaining.js +1 -1
  60. package/lib/rules/no-unused-vars.js +6 -8
  61. package/lib/rules/no-useless-assignment.js +566 -0
  62. package/lib/rules/no-useless-backreference.js +1 -1
  63. package/lib/rules/object-curly-spacing.js +3 -3
  64. package/lib/rules/object-property-newline.js +1 -1
  65. package/lib/rules/one-var.js +5 -5
  66. package/lib/rules/padded-blocks.js +7 -7
  67. package/lib/rules/prefer-arrow-callback.js +3 -3
  68. package/lib/rules/prefer-reflect.js +1 -1
  69. package/lib/rules/prefer-regex-literals.js +1 -1
  70. package/lib/rules/prefer-template.js +1 -1
  71. package/lib/rules/radix.js +2 -2
  72. package/lib/rules/semi-style.js +1 -1
  73. package/lib/rules/sort-imports.js +1 -1
  74. package/lib/rules/sort-keys.js +1 -1
  75. package/lib/rules/sort-vars.js +1 -1
  76. package/lib/rules/space-unary-ops.js +1 -1
  77. package/lib/rules/strict.js +1 -1
  78. package/lib/rules/utils/ast-utils.js +7 -7
  79. package/lib/rules/yield-star-spacing.js +1 -1
  80. package/lib/source-code/source-code.js +4 -4
  81. package/lib/source-code/token-store/index.js +2 -2
  82. package/package.json +4 -4
  83. package/conf/config-schema.js +0 -93
  84. package/lib/shared/config-validator.js +0 -380
  85. package/lib/shared/relative-module-resolver.js +0 -50
package/README.md CHANGED
@@ -245,6 +245,11 @@ The people who review and fix bugs and help triage issues.
245
245
  Bryan Mishkin
246
246
  </a>
247
247
  </td><td align="center" valign="top" width="11%">
248
+ <a href="https://github.com/JoshuaKGoldberg">
249
+ <img src="https://github.com/JoshuaKGoldberg.png?s=75" width="75" height="75" alt="Josh Goldberg ✨'s Avatar"><br />
250
+ Josh Goldberg ✨
251
+ </a>
252
+ </td><td align="center" valign="top" width="11%">
248
253
  <a href="https://github.com/fasttime">
249
254
  <img src="https://github.com/fasttime.png?s=75" width="75" height="75" alt="Francesco Trotta's Avatar"><br />
250
255
  Francesco Trotta
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @fileoverview Configuration related to ECMAScript versions
3
+ * @author Milos Djermanovic
4
+ */
5
+
6
+ "use strict";
7
+
8
+ /**
9
+ * The latest ECMAScript version supported by ESLint.
10
+ * @type {number} year-based ECMAScript version
11
+ */
12
+ const LATEST_ECMA_VERSION = 2024;
13
+
14
+ module.exports = {
15
+ LATEST_ECMA_VERSION
16
+ };
@@ -732,7 +732,7 @@ class CLIEngine {
732
732
  * @returns {void}
733
733
  */
734
734
  static outputFixes(report) {
735
- report.results.filter(result => Object.prototype.hasOwnProperty.call(result, "output")).forEach(result => {
735
+ report.results.filter(result => Object.hasOwn(result, "output")).forEach(result => {
736
736
  fs.writeFileSync(result.filePath, result.output);
737
737
  });
738
738
  }
@@ -164,7 +164,7 @@ class LintResultCache {
164
164
  * @returns {void}
165
165
  */
166
166
  setCachedLintResults(filePath, config, result) {
167
- if (result && Object.prototype.hasOwnProperty.call(result, "output")) {
167
+ if (result && Object.hasOwn(result, "output")) {
168
168
  return;
169
169
  }
170
170
 
@@ -181,7 +181,7 @@ class LintResultCache {
181
181
  * In `getCachedLintResults`, if source is explicitly null, we will
182
182
  * read the file from the filesystem to set the value again.
183
183
  */
184
- if (Object.prototype.hasOwnProperty.call(resultToSerialize, "source")) {
184
+ if (Object.hasOwn(resultToSerialize, "source")) {
185
185
  resultToSerialize.source = null;
186
186
  }
187
187
 
package/lib/cli.js CHANGED
@@ -285,25 +285,23 @@ async function printResults(engine, results, format, outputFile, resultsMeta) {
285
285
 
286
286
  const output = await formatter.format(results, resultsMeta);
287
287
 
288
- if (output) {
289
- if (outputFile) {
290
- const filePath = path.resolve(process.cwd(), outputFile);
288
+ if (outputFile) {
289
+ const filePath = path.resolve(process.cwd(), outputFile);
291
290
 
292
- if (await isDirectory(filePath)) {
293
- log.error("Cannot write to output file path, it is a directory: %s", outputFile);
294
- return false;
295
- }
291
+ if (await isDirectory(filePath)) {
292
+ log.error("Cannot write to output file path, it is a directory: %s", outputFile);
293
+ return false;
294
+ }
296
295
 
297
- try {
298
- await mkdir(path.dirname(filePath), { recursive: true });
299
- await writeFile(filePath, output);
300
- } catch (ex) {
301
- log.error("There was a problem writing the output file:\n%s", ex);
302
- return false;
303
- }
304
- } else {
305
- log.info(output);
296
+ try {
297
+ await mkdir(path.dirname(filePath), { recursive: true });
298
+ await writeFile(filePath, output);
299
+ } catch (ex) {
300
+ log.error("There was a problem writing the output file:\n%s", ex);
301
+ return false;
306
302
  }
303
+ } else if (output) {
304
+ log.info(output);
307
305
  }
308
306
 
309
307
  return true;
@@ -171,7 +171,7 @@ function createCommentRemoval(directives, commentToken) {
171
171
  return {
172
172
  description: ruleIds.length <= 2
173
173
  ? ruleIds.join(" or ")
174
- : `${ruleIds.slice(0, ruleIds.length - 1).join(", ")}, or ${ruleIds[ruleIds.length - 1]}`,
174
+ : `${ruleIds.slice(0, ruleIds.length - 1).join(", ")}, or ${ruleIds.at(-1)}`,
175
175
  fix: {
176
176
  range,
177
177
  text: " "
@@ -342,7 +342,7 @@ function applyDirectives(options) {
342
342
  problem.suppressions = problem.suppressions.concat(suppressions);
343
343
  } else {
344
344
  problem.suppressions = suppressions;
345
- usedDisableDirectives.add(disableDirectivesForProblem[disableDirectivesForProblem.length - 1]);
345
+ usedDisableDirectives.add(disableDirectivesForProblem.at(-1));
346
346
  }
347
347
  }
348
348
 
@@ -125,20 +125,6 @@ class CodePath {
125
125
  return this.internal.thrownForkContext;
126
126
  }
127
127
 
128
- /**
129
- * Tracks the traversal of the code path through each segment. This array
130
- * starts empty and segments are added or removed as the code path is
131
- * traversed. This array always ends up empty at the end of a code path
132
- * traversal. The `CodePathState` uses this to track its progress through
133
- * the code path.
134
- * This is a passthrough to the underlying `CodePathState`.
135
- * @type {CodePathSegment[]}
136
- * @deprecated
137
- */
138
- get currentSegments() {
139
- return this.internal.currentSegments;
140
- }
141
-
142
128
  /**
143
129
  * Traverses all segments in this code path.
144
130
  *
@@ -180,9 +166,9 @@ class CodePath {
180
166
  const lastSegment = resolvedOptions.last;
181
167
 
182
168
  // set up initial location information
183
- let record = null;
184
- let index = 0;
185
- let end = 0;
169
+ let record;
170
+ let index;
171
+ let end;
186
172
  let segment = null;
187
173
 
188
174
  // segments that have already been visited during traversal
@@ -210,7 +196,7 @@ class CodePath {
210
196
  if (stack.length <= 1) {
211
197
  broken = true;
212
198
  } else {
213
- skippedSegment = stack[stack.length - 2][0];
199
+ skippedSegment = stack.at(-2)[0];
214
200
  }
215
201
  },
216
202
 
@@ -251,7 +237,7 @@ class CodePath {
251
237
  * Otherwise, we just read the value and sometimes modify the
252
238
  * record as we traverse.
253
239
  */
254
- record = stack[stack.length - 1];
240
+ record = stack.at(-1);
255
241
  segment = record[0];
256
242
  index = record[1];
257
243
 
@@ -207,7 +207,7 @@ class ForkContext {
207
207
  get head() {
208
208
  const list = this.segmentsList;
209
209
 
210
- return list.length === 0 ? [] : list[list.length - 1];
210
+ return list.length === 0 ? [] : list.at(-1);
211
211
  }
212
212
 
213
213
  /**
@@ -75,11 +75,9 @@ module.exports = class ConfigCommentParser {
75
75
  parseJsonConfig(string, location) {
76
76
  debug("Parsing JSON config");
77
77
 
78
- let items = {};
79
-
80
78
  // Parses a JSON-like comment by the same way as parsing CLI option.
81
79
  try {
82
- items = levn.parse("Object", string) || {};
80
+ const items = levn.parse("Object", string) || {};
83
81
 
84
82
  // Some tests say that it should ignore invalid comments such as `/*eslint no-alert:abc*/`.
85
83
  // Also, commaless notations have invalid severity:
@@ -102,11 +100,15 @@ module.exports = class ConfigCommentParser {
102
100
  * Optionator cannot parse commaless notations.
103
101
  * But we are supporting that. So this is a fallback for that.
104
102
  */
105
- items = {};
106
103
  const normalizedString = string.replace(/([-a-zA-Z0-9/]+):/gu, "\"$1\":").replace(/(\]|[0-9])\s+(?=")/u, "$1,");
107
104
 
108
105
  try {
109
- items = JSON.parse(`{${normalizedString}}`);
106
+ const items = JSON.parse(`{${normalizedString}}`);
107
+
108
+ return {
109
+ success: true,
110
+ config: items
111
+ };
110
112
  } catch (ex) {
111
113
  debug("Manual parsing failed.");
112
114
 
@@ -124,11 +126,6 @@ module.exports = class ConfigCommentParser {
124
126
  };
125
127
 
126
128
  }
127
-
128
- return {
129
- success: true,
130
- config: items
131
- };
132
129
  }
133
130
 
134
131
  /**
@@ -52,6 +52,7 @@ const DEFAULT_ECMA_VERSION = 5;
52
52
  const commentParser = new ConfigCommentParser();
53
53
  const DEFAULT_ERROR_LOC = { start: { line: 1, column: 0 }, end: { line: 1, column: 1 } };
54
54
  const parserSymbol = Symbol.for("eslint.RuleTester.parser");
55
+ const { LATEST_ECMA_VERSION } = require("../../conf/ecma-version");
55
56
 
56
57
  //------------------------------------------------------------------------------
57
58
  // Typedefs
@@ -231,7 +232,7 @@ function addDeclaredGlobals(globalScope, configGlobals, { exportedVariables, ena
231
232
  * @private
232
233
  */
233
234
  function createMissingRuleMessage(ruleId) {
234
- return Object.prototype.hasOwnProperty.call(ruleReplacements.rules, ruleId)
235
+ return Object.hasOwn(ruleReplacements.rules, ruleId)
235
236
  ? `Rule '${ruleId}' was removed and replaced by: ${ruleReplacements.rules[ruleId].join(", ")}`
236
237
  : `Definition for rule '${ruleId}' was not found.`;
237
238
  }
@@ -594,7 +595,7 @@ function normalizeEcmaVersionForLanguageOptions(ecmaVersion) {
594
595
  * that is used for a number of processes inside of ESLint. It's normally
595
596
  * safe to assume people want the latest unless otherwise specified.
596
597
  */
597
- return espree.latestEcmaVersion + 2009;
598
+ return LATEST_ECMA_VERSION;
598
599
  }
599
600
 
600
601
  const eslintEnvPattern = /\/\*\s*eslint-env\s(.+?)(?:\*\/|$)/gsu;
@@ -1331,7 +1332,56 @@ class Linter {
1331
1332
  { exportedVariables: commentDirectives.exportedVariables, enabledGlobals: commentDirectives.enabledGlobals }
1332
1333
  );
1333
1334
 
1335
+ /*
1336
+ * Now we determine the final configurations for rules.
1337
+ * First, let all inline rule configurations override those from the config.
1338
+ * Then, check for a special case: if a rule is configured in both places,
1339
+ * inline rule configuration that only has severity should retain options from
1340
+ * the config and just override the severity.
1341
+ *
1342
+ * Example:
1343
+ *
1344
+ * {
1345
+ * rules: {
1346
+ * curly: ["error", "multi"]
1347
+ * }
1348
+ * }
1349
+ *
1350
+ * /* eslint curly: ["warn"] * /
1351
+ *
1352
+ * Results in:
1353
+ *
1354
+ * curly: ["warn", "multi"]
1355
+ */
1334
1356
  const configuredRules = Object.assign({}, config.rules, commentDirectives.configuredRules);
1357
+
1358
+ if (config.rules) {
1359
+ for (const [ruleId, ruleInlineConfig] of Object.entries(commentDirectives.configuredRules)) {
1360
+ if (
1361
+
1362
+ /*
1363
+ * If inline config for the rule has only severity
1364
+ */
1365
+ (!Array.isArray(ruleInlineConfig) || ruleInlineConfig.length === 1) &&
1366
+
1367
+ /*
1368
+ * And provided config for the rule has options
1369
+ */
1370
+ Object.hasOwn(config.rules, ruleId) &&
1371
+ (Array.isArray(config.rules[ruleId]) && config.rules[ruleId].length > 1)
1372
+ ) {
1373
+
1374
+ /*
1375
+ * Then use severity from the inline config and options from the provided config
1376
+ */
1377
+ configuredRules[ruleId] = [
1378
+ Array.isArray(ruleInlineConfig) ? ruleInlineConfig[0] : ruleInlineConfig, // severity from the inline config
1379
+ ...config.rules[ruleId].slice(1) // options from the provided config
1380
+ ];
1381
+ }
1382
+ }
1383
+ }
1384
+
1335
1385
  let lintingProblems;
1336
1386
 
1337
1387
  try {
@@ -1674,7 +1724,7 @@ class Linter {
1674
1724
  [ruleId]: ruleOptions
1675
1725
  }
1676
1726
  });
1677
- mergedInlineConfig.rules[ruleId] = ruleValue;
1727
+ mergedInlineConfig.rules[ruleId] = ruleOptions;
1678
1728
  } catch (err) {
1679
1729
 
1680
1730
  /*
@@ -1713,7 +1763,58 @@ class Linter {
1713
1763
  )
1714
1764
  : { problems: [], disableDirectives: [] };
1715
1765
 
1766
+ /*
1767
+ * Now we determine the final configurations for rules.
1768
+ * First, let all inline rule configurations override those from the config.
1769
+ * Then, check for a special case: if a rule is configured in both places,
1770
+ * inline rule configuration that only has severity should retain options from
1771
+ * the config and just override the severity.
1772
+ *
1773
+ * Example:
1774
+ *
1775
+ * {
1776
+ * rules: {
1777
+ * curly: ["error", "multi"]
1778
+ * }
1779
+ * }
1780
+ *
1781
+ * /* eslint curly: ["warn"] * /
1782
+ *
1783
+ * Results in:
1784
+ *
1785
+ * curly: ["warn", "multi"]
1786
+ *
1787
+ * At this point, all rule configurations are normalized to arrays.
1788
+ */
1716
1789
  const configuredRules = Object.assign({}, config.rules, mergedInlineConfig.rules);
1790
+
1791
+ if (config.rules) {
1792
+ for (const [ruleId, ruleInlineConfig] of Object.entries(mergedInlineConfig.rules)) {
1793
+ if (
1794
+
1795
+ /*
1796
+ * If inline config for the rule has only severity
1797
+ */
1798
+ ruleInlineConfig.length === 1 &&
1799
+
1800
+ /*
1801
+ * And provided config for the rule has options
1802
+ */
1803
+ Object.hasOwn(config.rules, ruleId) &&
1804
+ config.rules[ruleId].length > 1
1805
+ ) {
1806
+
1807
+ /*
1808
+ * Then use severity from the inline config and options from the provided config
1809
+ */
1810
+ configuredRules[ruleId] = [
1811
+ ruleInlineConfig[0], // severity from the inline config
1812
+ ...config.rules[ruleId].slice(1) // options from the provided config
1813
+ ];
1814
+ }
1815
+ }
1816
+ }
1817
+
1717
1818
  let lintingProblems;
1718
1819
 
1719
1820
  sourceCode.finalize();
@@ -2039,7 +2140,7 @@ class Linter {
2039
2140
  * SourceCodeFixer.
2040
2141
  */
2041
2142
  verifyAndFix(text, config, options) {
2042
- let messages = [],
2143
+ let messages,
2043
2144
  fixedResult,
2044
2145
  fixed = false,
2045
2146
  passNumber = 0,
@@ -160,7 +160,7 @@ function mergeFixes(fixes, sourceCode) {
160
160
 
161
161
  const originalText = sourceCode.text;
162
162
  const start = fixes[0].range[0];
163
- const end = fixes[fixes.length - 1].range[1];
163
+ const end = fixes.at(-1).range[1];
164
164
  let text = "";
165
165
  let lastPos = Number.MIN_SAFE_INTEGER;
166
166
 
@@ -343,7 +343,7 @@ module.exports = function createReportTranslator(metadata) {
343
343
  if (descriptor.message) {
344
344
  throw new TypeError("context.report() called with a message and a messageId. Please only pass one.");
345
345
  }
346
- if (!messages || !Object.prototype.hasOwnProperty.call(messages, id)) {
346
+ if (!messages || !Object.hasOwn(messages, id)) {
347
347
  throw new TypeError(`context.report() called with a messageId of '${id}' which is not present in the 'messages' config: ${JSON.stringify(messages, null, 2)}`);
348
348
  }
349
349
  computedMessage = messages[id];
@@ -107,7 +107,7 @@ SourceCodeFixer.applyFixes = function(sourceText, messages, shouldFix) {
107
107
  }
108
108
 
109
109
  messages.forEach(problem => {
110
- if (Object.prototype.hasOwnProperty.call(problem, "fix")) {
110
+ if (Object.hasOwn(problem, "fix")) {
111
111
  fixes.push(problem);
112
112
  } else {
113
113
  remainingMessages.push(problem);
@@ -16,8 +16,7 @@ const
16
16
  equal = require("fast-deep-equal"),
17
17
  Traverser = require("../shared/traverser"),
18
18
  { getRuleOptionsSchema } = require("../config/flat-config-helpers"),
19
- { Linter, SourceCodeFixer, interpolate } = require("../linter"),
20
- CodePath = require("../linter/code-path-analysis/code-path");
19
+ { Linter, SourceCodeFixer, interpolate } = require("../linter");
21
20
 
22
21
  const { FlatConfigArray } = require("../config/flat-config-array");
23
22
  const { defaultConfig } = require("../config/default-config");
@@ -274,21 +273,6 @@ function wrapParser(parser) {
274
273
  };
275
274
  }
276
275
 
277
- /**
278
- * Emit a deprecation warning if rule uses CodePath#currentSegments.
279
- * @param {string} ruleName Name of the rule.
280
- * @returns {void}
281
- */
282
- function emitCodePathCurrentSegmentsWarning(ruleName) {
283
- if (!emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`]) {
284
- emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`] = true;
285
- process.emitWarning(
286
- `"${ruleName}" rule uses CodePath#currentSegments and will stop working in ESLint v9. Please read the documentation for how to update your code: https://eslint.org/docs/latest/extend/code-path-analysis#usage-examples`,
287
- "DeprecationWarning"
288
- );
289
- }
290
- }
291
-
292
276
  /**
293
277
  * Function to replace forbidden `SourceCode` methods. Allows just one call per method.
294
278
  * @param {string} methodName The name of the method to forbid.
@@ -753,24 +737,15 @@ class RuleTester {
753
737
 
754
738
  // Verify the code.
755
739
  const { applyLanguageOptions, applyInlineConfig, finalize } = SourceCode.prototype;
756
- const originalCurrentSegments = Object.getOwnPropertyDescriptor(CodePath.prototype, "currentSegments");
757
740
  let messages;
758
741
 
759
742
  try {
760
- Object.defineProperty(CodePath.prototype, "currentSegments", {
761
- get() {
762
- emitCodePathCurrentSegmentsWarning(ruleName);
763
- return originalCurrentSegments.get.call(this);
764
- }
765
- });
766
-
767
743
  forbiddenMethods.forEach(methodName => {
768
744
  SourceCode.prototype[methodName] = throwForbiddenMethodError(methodName, SourceCode.prototype);
769
745
  });
770
746
 
771
747
  messages = linter.verify(code, configs, filename);
772
748
  } finally {
773
- Object.defineProperty(CodePath.prototype, "currentSegments", originalCurrentSegments);
774
749
  SourceCode.prototype.applyInlineConfig = applyInlineConfig;
775
750
  SourceCode.prototype.applyLanguageOptions = applyLanguageOptions;
776
751
  SourceCode.prototype.finalize = finalize;
@@ -74,7 +74,7 @@ module.exports = {
74
74
  function normalizeOptionValue(option) {
75
75
  let consistent = false;
76
76
  let multiline = false;
77
- let minItems = 0;
77
+ let minItems;
78
78
 
79
79
  if (option) {
80
80
  if (option === "consistent") {
@@ -199,7 +199,7 @@ module.exports = {
199
199
  : sourceCode.getLastToken(node),
200
200
  penultimate = sourceCode.getTokenBefore(last),
201
201
  firstElement = node.elements[0],
202
- lastElement = node.elements[node.elements.length - 1];
202
+ lastElement = node.elements.at(-1);
203
203
 
204
204
  const openingBracketMustBeSpaced =
205
205
  options.objectsInArraysException && isObjectType(firstElement) ||
@@ -79,7 +79,7 @@ module.exports = {
79
79
  }
80
80
 
81
81
  // Defines a predicate to check whether or not a given reference is outside of valid scope.
82
- const scopeRange = stack[stack.length - 1];
82
+ const scopeRange = stack.at(-1);
83
83
 
84
84
  /**
85
85
  * Check if a reference is out of scope
@@ -147,7 +147,7 @@ module.exports = {
147
147
  if (closestBlock.type === "BlockStatement") {
148
148
 
149
149
  // find the last item in the block
150
- const lastItem = closestBlock.body[closestBlock.body.length - 1];
150
+ const lastItem = closestBlock.body.at(-1);
151
151
 
152
152
  // if the callback is the last thing in a block that might be ok
153
153
  if (isCallbackExpression(node, lastItem)) {
@@ -168,7 +168,7 @@ module.exports = {
168
168
  if (lastItem.type === "ReturnStatement") {
169
169
 
170
170
  // but only if the callback is immediately before
171
- if (isCallbackExpression(node, closestBlock.body[closestBlock.body.length - 2])) {
171
+ if (isCallbackExpression(node, closestBlock.body.at(-2))) {
172
172
  return;
173
173
  }
174
174
  }
@@ -154,7 +154,7 @@ module.exports = {
154
154
  * @returns {any} The last element
155
155
  */
156
156
  function last(array) {
157
- return array[array.length - 1];
157
+ return array.at(-1);
158
158
  }
159
159
 
160
160
  switch (node.type) {
@@ -66,7 +66,7 @@ module.exports = {
66
66
  NewExpression: true
67
67
  };
68
68
 
69
- if (context.options.length === 2 && Object.prototype.hasOwnProperty.call(context.options[1], "exceptions")) {
69
+ if (context.options.length === 2 && Object.hasOwn(context.options[1], "exceptions")) {
70
70
  const keys = Object.keys(context.options[1].exceptions);
71
71
 
72
72
  for (let i = 0; i < keys.length; i++) {
@@ -218,7 +218,7 @@ module.exports = {
218
218
 
219
219
  previousItemToken = tokenAfterItem
220
220
  ? sourceCode.getTokenBefore(tokenAfterItem)
221
- : sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1];
221
+ : sourceCode.ast.tokens.at(-1);
222
222
  } else {
223
223
  previousItemToken = currentItemToken;
224
224
  }
@@ -64,7 +64,7 @@ module.exports = {
64
64
 
65
65
  if (
66
66
  typeof option === "object" &&
67
- (Object.prototype.hasOwnProperty.call(option, "maximum") || Object.prototype.hasOwnProperty.call(option, "max"))
67
+ (Object.hasOwn(option, "maximum") || Object.hasOwn(option, "max"))
68
68
  ) {
69
69
  THRESHOLD = option.maximum || option.max;
70
70
  } else if (typeof option === "number") {
@@ -109,7 +109,7 @@ function isPossibleConstructor(node) {
109
109
  );
110
110
 
111
111
  case "SequenceExpression": {
112
- const lastExpression = node.expressions[node.expressions.length - 1];
112
+ const lastExpression = node.expressions.at(-1);
113
113
 
114
114
  return isPossibleConstructor(lastExpression);
115
115
  }
@@ -54,7 +54,7 @@ module.exports = {
54
54
  * @returns {any} Last element
55
55
  */
56
56
  function last(collection) {
57
- return collection[collection.length - 1];
57
+ return collection.at(-1);
58
58
  }
59
59
 
60
60
  //--------------------------------------------------------------------------
@@ -45,7 +45,7 @@ module.exports = {
45
45
  Program: function checkBadEOF(node) {
46
46
  const sourceCode = context.sourceCode,
47
47
  src = sourceCode.getText(),
48
- lastLine = sourceCode.lines[sourceCode.lines.length - 1],
48
+ lastLine = sourceCode.lines.at(-1),
49
49
  location = {
50
50
  column: lastLine.length,
51
51
  line: sourceCode.lines.length
@@ -89,7 +89,7 @@ module.exports = {
89
89
  });
90
90
  } else if (mode === "never" && endsWithNewline) {
91
91
 
92
- const secondLastLine = sourceCode.lines[sourceCode.lines.length - 2];
92
+ const secondLastLine = sourceCode.lines.at(-2);
93
93
 
94
94
  // File is newline-terminated, but shouldn't be
95
95
  context.report({
@@ -218,7 +218,7 @@ module.exports = {
218
218
  case "FunctionExpression": {
219
219
  const leftParen = sourceCode.getFirstToken(node, astUtils.isOpeningParenToken);
220
220
  const rightParen = node.params.length
221
- ? sourceCode.getTokenAfter(node.params[node.params.length - 1], astUtils.isClosingParenToken)
221
+ ? sourceCode.getTokenAfter(node.params.at(-1), astUtils.isClosingParenToken)
222
222
  : sourceCode.getTokenAfter(leftParen);
223
223
 
224
224
  return { leftParen, rightParen };
@@ -234,7 +234,7 @@ module.exports = {
234
234
  }
235
235
 
236
236
  const rightParen = node.params.length
237
- ? sourceCode.getTokenAfter(node.params[node.params.length - 1], astUtils.isClosingParenToken)
237
+ ? sourceCode.getTokenAfter(node.params.at(-1), astUtils.isClosingParenToken)
238
238
  : sourceCode.getTokenAfter(firstToken);
239
239
 
240
240
  return {
@@ -789,7 +789,7 @@ module.exports = {
789
789
  if (elements.length > 0) {
790
790
 
791
791
  // Skip last block line check if last item in same line
792
- if (elements[elements.length - 1].loc.end.line === node.loc.end.line) {
792
+ if (elements.at(-1).loc.end.line === node.loc.end.line) {
793
793
  return;
794
794
  }
795
795
  }
@@ -830,7 +830,7 @@ module.exports = {
830
830
  }
831
831
 
832
832
  let indent;
833
- let nodesToCheck = [];
833
+ let nodesToCheck;
834
834
 
835
835
  /*
836
836
  * For this statements we should check indent from statement beginning,
@@ -873,7 +873,7 @@ module.exports = {
873
873
  */
874
874
  function filterOutSameLineVars(node) {
875
875
  return node.declarations.reduce((finalCollection, elem) => {
876
- const lastElem = finalCollection[finalCollection.length - 1];
876
+ const lastElem = finalCollection.at(-1);
877
877
 
878
878
  if ((elem.loc.start.line !== node.loc.start.line && !lastElem) ||
879
879
  (lastElem && lastElem.loc.start.line !== elem.loc.start.line)) {
@@ -892,7 +892,7 @@ module.exports = {
892
892
  function checkIndentInVariableDeclarations(node) {
893
893
  const elements = filterOutSameLineVars(node);
894
894
  const nodeIndent = getNodeIndent(node).goodChar;
895
- const lastElement = elements[elements.length - 1];
895
+ const lastElement = elements.at(-1);
896
896
 
897
897
  const elementsIndent = nodeIndent + indentSize * options.VariableDeclarator[node.kind];
898
898
 
@@ -999,7 +999,7 @@ module.exports = {
999
999
  },
1000
1000
 
1001
1001
  VariableDeclaration(node) {
1002
- if (node.declarations[node.declarations.length - 1].loc.start.line > node.declarations[0].loc.start.line) {
1002
+ if (node.declarations.at(-1).loc.start.line > node.declarations[0].loc.start.line) {
1003
1003
  checkIndentInVariableDeclarations(node);
1004
1004
  }
1005
1005
  },