Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(deps): bump eslint from 8.57.0 to 9.18.0 #1838

Closed
wants to merge 1 commit into from

Conversation

dependabot[bot]
Copy link
Contributor

@dependabot dependabot bot commented on behalf of github Jan 13, 2025

Bumps eslint from 8.57.0 to 9.18.0.

Release notes

Sourced from eslint's releases.

v9.18.0

Features

  • e84e6e2 feat: Report allowed methods for no-console rule (#19306) (Anna Bocharova)
  • 8efc2d0 feat: unflag TypeScript config files (#19266) (Francesco Trotta)
  • 87a9352 feat: check imports and class names in no-shadow-restricted-names (#19272) (Milos Djermanovic)

Bug Fixes

  • da768d4 fix: correct overrideConfigFile type (#19289) (Francesco Trotta)

Documentation

  • d9c23c5 docs: replace var with const in rule examples (#19325) (Tanuj Kanti)
  • 8e1a898 docs: add tabs to cli code blocks (#18784) (Jay)
  • f3aeefb docs: rewrite using let and const in rule examples (#19320) (PoloSpark)
  • 0b680b3 docs: Update README (GitHub Actions Bot)
  • 98c86a9 docs: Edit this page button link to different branches (#19228) (Tanuj Kanti)
  • 6947901 docs: remove hardcoded edit link (#19323) (Milos Djermanovic)
  • 03f2f44 docs: rewrite var with const in rules examples (#19317) (Thiago)
  • 26c3003 docs: Clarify dangers of eslint:all (#19318) (Nicholas C. Zakas)
  • c038257 docs: add eqeqeq in related rules to no-eq-null (#19310) (루밀LuMir)
  • 89c8fc5 docs: rewrite examples with var using let and const (#19315) (Amaresh S M)
  • db574c4 docs: add missing backticks to no-void (#19313) (루밀LuMir)
  • 8d943c3 docs: add missing backticks to default-case-last (#19311) (루밀LuMir)
  • 36ef8bb docs: rewrite examples with var using let and const (#19298) (Amaresh S M)
  • 1610c9e docs: add missing backticks to no-else-return (#19309) (루밀LuMir)
  • df409d8 docs: Update README (GitHub Actions Bot)
  • 2e84213 docs: Fix Horizontal Scroll Overflow in Rule Description on Mobile View (#19304) (Amaresh S M)
  • 6e7361b docs: replace var with let and const in rule example (#19302) (Tanuj Kanti)
  • 069af5e docs: rewrite var using const in rule examples (#19303) (Kim GyeonWon)
  • 064e35d docs: remove 'I hope to' comments from scope-manager-interface (#19300) (Josh Goldberg ✨)
  • 8e00305 docs: replace var with const in rule examples (#19299) (Tanuj Kanti)
  • a559009 docs: Add warning about extending core rules (#19295) (Nicholas C. Zakas)
  • 0bfdf6c docs: Update README (GitHub Actions Bot)
  • ce0b9ff docs: add navigation link for code explorer (#19285) (Tanuj Kanti)
  • e255cc9 docs: add bluesky icon to footer (#19290) (Tanuj Kanti)
  • 5d64851 docs: remove outdated info about environments (#19296) (Francesco Trotta)
  • eec01f0 docs: switch rule examples config format to languageOptions (#19277) (Milos Djermanovic)
  • b36ca0a docs: Fixing Focus Order by Rearranging Element Sequence (#19241) (Amaresh S M)
  • d122c8a docs: add missing backticks to sort-imports (#19282) (루밀LuMir)
  • 0367a70 docs: update custom parser docs (#19288) (Francesco Trotta)
  • 8c07ebb docs: add border-radius to hX:target selector styles (#19270) (루밀LuMir)
  • eff7c57 docs: add limitation section in no-loop-func (#19287) (Tanuj Kanti)
  • 5db226f docs: add missing backticks in various parts of the documentation (#19269) (루밀LuMir)
  • 789edbb docs: Update README (GitHub Actions Bot)
  • 613c06a docs: mark rules that are frozen with ❄️ (#19231) (Amaresh S M)
  • 43172ec docs: Update README (GitHub Actions Bot)
  • ac8b3c4 docs: fix description of overrideConfigFile option (#19262) (Milos Djermanovic)
  • bbb9b46 docs: Update README (GitHub Actions Bot)
  • 995b492 docs: fix inconsistent divider in rule categories box (#19249) (Tanuj Kanti)
  • f76d05d docs: Refactor search result handling with better event listener cleanup (#19252) (Amaresh S M)
  • c5f3d7d docs: Update README (GitHub Actions Bot)

... (truncated)

Changelog

Sourced from eslint's changelog.

v9.18.0 - January 10, 2025

  • c52be85 chore: upgrade to @eslint/[email protected] (#19330) (Francesco Trotta)
  • 362099c chore: package.json update for @​eslint/js release (Jenkins)
  • 9486141 deps: upgrade @eslint/core and @eslint/plugin-kit (#19329) (Francesco Trotta)
  • d9c23c5 docs: replace var with const in rule examples (#19325) (Tanuj Kanti)
  • 8e1a898 docs: add tabs to cli code blocks (#18784) (Jay)
  • f3aeefb docs: rewrite using let and const in rule examples (#19320) (PoloSpark)
  • 0b680b3 docs: Update README (GitHub Actions Bot)
  • 98c86a9 docs: Edit this page button link to different branches (#19228) (Tanuj Kanti)
  • 6947901 docs: remove hardcoded edit link (#19323) (Milos Djermanovic)
  • 03f2f44 docs: rewrite var with const in rules examples (#19317) (Thiago)
  • 26c3003 docs: Clarify dangers of eslint:all (#19318) (Nicholas C. Zakas)
  • c038257 docs: add eqeqeq in related rules to no-eq-null (#19310) (루밀LuMir)
  • 89c8fc5 docs: rewrite examples with var using let and const (#19315) (Amaresh S M)
  • 495aa49 chore: extract package name from package.json for public interface (#19314) (루밀LuMir)
  • db574c4 docs: add missing backticks to no-void (#19313) (루밀LuMir)
  • 8d943c3 docs: add missing backticks to default-case-last (#19311) (루밀LuMir)
  • 36ef8bb docs: rewrite examples with var using let and const (#19298) (Amaresh S M)
  • 1610c9e docs: add missing backticks to no-else-return (#19309) (루밀LuMir)
  • df409d8 docs: Update README (GitHub Actions Bot)
  • e84e6e2 feat: Report allowed methods for no-console rule (#19306) (Anna Bocharova)
  • 2e84213 docs: Fix Horizontal Scroll Overflow in Rule Description on Mobile View (#19304) (Amaresh S M)
  • 6e7361b docs: replace var with let and const in rule example (#19302) (Tanuj Kanti)
  • 069af5e docs: rewrite var using const in rule examples (#19303) (Kim GyeonWon)
  • 064e35d docs: remove 'I hope to' comments from scope-manager-interface (#19300) (Josh Goldberg ✨)
  • 8e00305 docs: replace var with const in rule examples (#19299) (Tanuj Kanti)
  • a559009 docs: Add warning about extending core rules (#19295) (Nicholas C. Zakas)
  • 0bfdf6c docs: Update README (GitHub Actions Bot)
  • ce0b9ff docs: add navigation link for code explorer (#19285) (Tanuj Kanti)
  • e255cc9 docs: add bluesky icon to footer (#19290) (Tanuj Kanti)
  • 5d64851 docs: remove outdated info about environments (#19296) (Francesco Trotta)
  • eec01f0 docs: switch rule examples config format to languageOptions (#19277) (Milos Djermanovic)
  • b36ca0a docs: Fixing Focus Order by Rearranging Element Sequence (#19241) (Amaresh S M)
  • d122c8a docs: add missing backticks to sort-imports (#19282) (루밀LuMir)
  • 0367a70 docs: update custom parser docs (#19288) (Francesco Trotta)
  • da768d4 fix: correct overrideConfigFile type (#19289) (Francesco Trotta)
  • 8c07ebb docs: add border-radius to hX:target selector styles (#19270) (루밀LuMir)
  • eff7c57 docs: add limitation section in no-loop-func (#19287) (Tanuj Kanti)
  • 8efc2d0 feat: unflag TypeScript config files (#19266) (Francesco Trotta)
  • 87a9352 feat: check imports and class names in no-shadow-restricted-names (#19272) (Milos Djermanovic)
  • 5db226f docs: add missing backticks in various parts of the documentation (#19269) (루밀LuMir)
  • 789edbb docs: Update README (GitHub Actions Bot)
  • 613c06a docs: mark rules that are frozen with ❄️ (#19231) (Amaresh S M)
  • 43172ec docs: Update README (GitHub Actions Bot)
  • ac8b3c4 docs: fix description of overrideConfigFile option (#19262) (Milos Djermanovic)
  • 6fe0e72 chore: update dependency @​eslint/json to ^0.9.0 (#19263) (renovate[bot])
  • bbb9b46 docs: Update README (GitHub Actions Bot)
  • 995b492 docs: fix inconsistent divider in rule categories box (#19249) (Tanuj Kanti)
  • f76d05d docs: Refactor search result handling with better event listener cleanup (#19252) (Amaresh S M)

... (truncated)

Commits

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot merge will merge this PR after your CI passes on it
  • @dependabot squash and merge will squash and merge this PR after your CI passes on it
  • @dependabot cancel merge will cancel a previously requested merge and block automerging
  • @dependabot reopen will reopen this PR if it is closed
  • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

@dependabot dependabot bot added dependencies Pull requests that update a dependency file javascript Pull requests that update Javascript code labels Jan 13, 2025
Copy link
Contributor

Diff between eslint 8.57.0 and 9.18.0
diff --git a/lib/rules/accessor-pairs.js b/lib/rules/accessor-pairs.js
index v8.57.0..v9.18.0 100644
--- a/lib/rules/accessor-pairs.js
+++ b/lib/rules/accessor-pairs.js
@@ -140,4 +140,10 @@
         type: "suggestion",
 
+        defaultOptions: [{
+            enforceForClassMembers: true,
+            getWithoutSet: false,
+            setWithoutGet: true
+        }],
+
         docs: {
             description: "Enforce getter and setter pairs in objects and classes",
@@ -150,14 +156,11 @@
             properties: {
                 getWithoutSet: {
-                    type: "boolean",
-                    default: false
+                    type: "boolean"
                 },
                 setWithoutGet: {
-                    type: "boolean",
-                    default: true
+                    type: "boolean"
                 },
                 enforceForClassMembers: {
-                    type: "boolean",
-                    default: true
+                    type: "boolean"
                 }
             },
@@ -175,8 +178,9 @@
     },
     create(context) {
-        const config = context.options[0] || {};
-        const checkGetWithoutSet = config.getWithoutSet === true;
-        const checkSetWithoutGet = config.setWithoutGet !== false;
-        const enforceForClassMembers = config.enforceForClassMembers !== false;
+        const [{
+            getWithoutSet: checkGetWithoutSet,
+            setWithoutGet: checkSetWithoutGet,
+            enforceForClassMembers
+        }] = context.options;
         const sourceCode = context.sourceCode;
 
diff --git a/lib/api.js b/lib/api.js
index v8.57.0..v9.18.0 100644
--- a/lib/api.js
+++ b/lib/api.js
@@ -10,9 +10,9 @@
 //-----------------------------------------------------------------------------
 
-const { ESLint, FlatESLint } = require("./eslint");
-const { shouldUseFlatConfig } = require("./eslint/flat-eslint");
+const { ESLint, shouldUseFlatConfig } = require("./eslint/eslint");
+const { LegacyESLint } = require("./eslint/legacy-eslint");
 const { Linter } = require("./linter");
 const { RuleTester } = require("./rule-tester");
-const { SourceCode } = require("./source-code");
+const { SourceCode } = require("./languages/js/source-code");
 
 //-----------------------------------------------------------------------------
@@ -24,20 +24,16 @@
  * @param {Object} [options] The options object
  * @param {boolean} [options.useFlatConfig] Whether or not to use a flat config
- * @param {string} [options.cwd] The current working directory
  * @returns {Promise<ESLint|LegacyESLint>} The ESLint constructor
  */
-async function loadESLint({ useFlatConfig, cwd = process.cwd() } = {}) {
+async function loadESLint({ useFlatConfig } = {}) {
 
     /*
-     * Note: The v9.x version of this function doesn't have a cwd option
-     * because it's not used. It's only used in the v8.x version of this
-     * function.
+     * Note: The v8.x version of this function also accepted a `cwd` option, but
+     * it is not used in this implementation so we silently ignore it.
      */
 
-    const shouldESLintUseFlatConfig = typeof useFlatConfig === "boolean"
-        ? useFlatConfig
-        : await shouldUseFlatConfig({ cwd });
+    const shouldESLintUseFlatConfig = useFlatConfig ?? (await shouldUseFlatConfig());
 
-    return shouldESLintUseFlatConfig ? FlatESLint : ESLint;
+    return shouldESLintUseFlatConfig ? ESLint : LegacyESLint;
 }
 
diff --git a/lib/linter/apply-disable-directives.js b/lib/linter/apply-disable-directives.js
index v8.57.0..v9.18.0 100644
--- a/lib/linter/apply-disable-directives.js
+++ b/lib/linter/apply-disable-directives.js
@@ -11,4 +11,7 @@
 
 /** @typedef {import("../shared/types").LintMessage} LintMessage */
+/** @typedef {import("@eslint/core").Language} Language */
+/** @typedef {import("@eslint/core").Position} Position */
+/** @typedef {import("@eslint/core").RulesConfig} RulesConfig */
 
 //------------------------------------------------------------------------------
@@ -17,9 +20,14 @@
 
 const escapeRegExp = require("escape-string-regexp");
+const {
+    Legacy: {
+        ConfigOps
+    }
+} = require("@eslint/eslintrc/universal");
 
 /**
  * Compares the locations of two objects in a source file
- * @param {{line: number, column: number}} itemA The first object
- * @param {{line: number, column: number}} itemB The second object
+ * @param {Position} itemA The first object
+ * @param {Position} itemB The second object
  * @returns {number} A value less than 1 if itemA appears before itemB in the source file, greater than 1 if
  * itemA appears after itemB in the source file, or 0 if itemA and itemB have the same location.
@@ -34,14 +42,14 @@
  * @returns {Directive[][]} Directives grouped by their parent comment.
  */
-function groupByParentComment(directives) {
+function groupByParentDirective(directives) {
     const groups = new Map();
 
     for (const directive of directives) {
-        const { unprocessedDirective: { parentComment } } = directive;
+        const { unprocessedDirective: { parentDirective } } = directive;
 
-        if (groups.has(parentComment)) {
-            groups.get(parentComment).push(directive);
+        if (groups.has(parentDirective)) {
+            groups.get(parentDirective).push(directive);
         } else {
-            groups.set(parentComment, [directive]);
+            groups.set(parentDirective, [directive]);
         }
     }
@@ -53,20 +61,12 @@
  * Creates removal details for a set of directives within the same comment.
  * @param {Directive[]} directives Unused directives to be removed.
- * @param {Token} commentToken The backing Comment token.
+ * @param {{node: Token, value: string}} parentDirective Data about the backing directive.
+ * @param {SourceCode} sourceCode The source code object for the file being linted.
  * @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
  */
-function createIndividualDirectivesRemoval(directives, commentToken) {
+function createIndividualDirectivesRemoval(directives, parentDirective, sourceCode) {
 
     /*
-     * `commentToken.value` starts right after `//` or `/*`.
-     * All calculated offsets will be relative to this index.
-     */
-    const commentValueStart = commentToken.range[0] + "//".length;
-
-    // Find where the list of rules starts. `\S+` matches with the directive name (e.g. `eslint-disable-line`)
-    const listStartOffset = /^\s*\S+\s+/u.exec(commentToken.value)[0].length;
-
-    /*
-     * Get the list text without any surrounding whitespace. In order to preserve the original
+     * Get the list of the rules text without any surrounding whitespace. In order to preserve the original
      * formatting, we don't want to change that whitespace.
      *
@@ -74,9 +74,9 @@
      *                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      */
-    const listText = commentToken.value
-        .slice(listStartOffset) // remove directive name and all whitespace before the list
-        .split(/\s-{2,}\s/u)[0] // remove `-- comment`, if it exists
-        .trimEnd(); // remove all whitespace after the list
+    const listText = parentDirective.value.trim();
 
+    // Calculate where it starts in the source code text
+    const listStart = sourceCode.text.indexOf(listText, sourceCode.getRange(parentDirective.node)[0]);
+
     /*
      * We can assume that `listText` contains multiple elements.
@@ -91,11 +91,11 @@
         const match = regex.exec(listText);
         const matchedText = match[0];
-        const matchStartOffset = listStartOffset + match.index;
-        const matchEndOffset = matchStartOffset + matchedText.length;
+        const matchStart = listStart + match.index;
+        const matchEnd = matchStart + matchedText.length;
 
         const firstIndexOfComma = matchedText.indexOf(",");
         const lastIndexOfComma = matchedText.lastIndexOf(",");
 
-        let removalStartOffset, removalEndOffset;
+        let removalStart, removalEnd;
 
         if (firstIndexOfComma !== lastIndexOfComma) {
@@ -113,6 +113,6 @@
              *                                     ^^^^^^^^^^^
              */
-            removalStartOffset = matchStartOffset + firstIndexOfComma;
-            removalEndOffset = matchStartOffset + lastIndexOfComma;
+            removalStart = matchStart + firstIndexOfComma;
+            removalEnd = matchStart + lastIndexOfComma;
 
         } else {
@@ -136,6 +136,6 @@
              *                                               ^^^^^^^^^^^^^
              */
-            removalStartOffset = matchStartOffset;
-            removalEndOffset = matchEndOffset;
+            removalStart = matchStart;
+            removalEnd = matchEnd;
         }
 
@@ -144,6 +144,6 @@
             fix: {
                 range: [
-                    commentValueStart + removalStartOffset,
-                    commentValueStart + removalEndOffset
+                    removalStart,
+                    removalEnd
                 ],
                 text: ""
@@ -155,11 +155,12 @@
 
 /**
- * Creates a description of deleting an entire unused disable comment.
+ * Creates a description of deleting an entire unused disable directive.
  * @param {Directive[]} directives Unused directives to be removed.
- * @param {Token} commentToken The backing Comment token.
- * @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output Problem.
+ * @param {Token} node The backing Comment token.
+ * @param {SourceCode} sourceCode The source code object for the file being linted.
+ * @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output problem.
  */
-function createCommentRemoval(directives, commentToken) {
-    const { range } = commentToken;
+function createDirectiveRemoval(directives, node, sourceCode) {
+    const range = sourceCode.getRange(node);
     const ruleIds = directives.filter(directive => directive.ruleId).map(directive => `'${directive.ruleId}'`);
 
@@ -167,5 +168,5 @@
         description: ruleIds.length <= 2
             ? ruleIds.join(" or ")
-            : `${ruleIds.slice(0, ruleIds.length - 1).join(", ")}, or ${ruleIds[ruleIds.length - 1]}`,
+            : `${ruleIds.slice(0, ruleIds.length - 1).join(", ")}, or ${ruleIds.at(-1)}`,
         fix: {
             range,
@@ -179,13 +180,14 @@
  * Parses details from directives to create output Problems.
  * @param {Iterable<Directive>} allDirectives Unused directives to be removed.
+ * @param {SourceCode} sourceCode The source code object for the file being linted.
  * @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
  */
-function processUnusedDirectives(allDirectives) {
-    const directiveGroups = groupByParentComment(allDirectives);
+function processUnusedDirectives(allDirectives, sourceCode) {
+    const directiveGroups = groupByParentDirective(allDirectives);
 
     return directiveGroups.flatMap(
         directives => {
-            const { parentComment } = directives[0].unprocessedDirective;
-            const remainingRuleIds = new Set(parentComment.ruleIds);
+            const { parentDirective } = directives[0].unprocessedDirective;
+            const remainingRuleIds = new Set(parentDirective.ruleIds);
 
             for (const directive of directives) {
@@ -194,6 +196,6 @@
 
             return remainingRuleIds.size
-                ? createIndividualDirectivesRemoval(directives, parentComment.commentToken)
-                : [createCommentRemoval(directives, parentComment.commentToken)];
+                ? createIndividualDirectivesRemoval(directives, parentDirective, sourceCode)
+                : [createDirectiveRemoval(directives, parentDirective.node, sourceCode)];
         }
     );
@@ -302,4 +304,5 @@
     const problems = [];
     const usedDisableDirectives = new Set();
+    const { sourceCode } = options;
 
     for (const problem of options.problems) {
@@ -338,5 +341,5 @@
             } else {
                 problem.suppressions = suppressions;
-                usedDisableDirectives.add(disableDirectivesForProblem[disableDirectivesForProblem.length - 1]);
+                usedDisableDirectives.add(disableDirectivesForProblem.at(-1));
             }
         }
@@ -346,9 +349,9 @@
 
     const unusedDisableDirectivesToReport = options.directives
-        .filter(directive => directive.type === "disable" && !usedDisableDirectives.has(directive));
+        .filter(directive => directive.type === "disable" && !usedDisableDirectives.has(directive) && !options.rulesToIgnore.has(directive.ruleId));
 
 
     const unusedEnableDirectivesToReport = new Set(
-        options.directives.filter(directive => directive.unprocessedDirective.type === "enable")
+        options.directives.filter(directive => directive.unprocessedDirective.type === "enable" && !options.rulesToIgnore.has(directive.ruleId))
     );
 
@@ -363,10 +366,12 @@
     }
 
-    const processed = processUnusedDirectives(unusedDisableDirectivesToReport)
-        .concat(processUnusedDirectives(unusedEnableDirectivesToReport));
+    const processed = processUnusedDirectives(unusedDisableDirectivesToReport, sourceCode)
+        .concat(processUnusedDirectives(unusedEnableDirectivesToReport, sourceCode));
+    const columnOffset = options.language.columnStart === 1 ? 0 : 1;
+    const lineOffset = options.language.lineStart === 1 ? 0 : 1;
 
     const unusedDirectives = processed
         .map(({ description, fix, unprocessedDirective }) => {
-            const { parentComment, type, line, column } = unprocessedDirective;
+            const { parentDirective, type, line, column } = unprocessedDirective;
 
             let message;
@@ -381,9 +386,12 @@
                     : "Unused eslint-disable directive (no problems were reported).";
             }
+
+            const loc = sourceCode.getLoc(parentDirective.node);
+
             return {
                 ruleId: null,
                 message,
-                line: type === "disable-next-line" ? parentComment.commentToken.loc.start.line : line,
-                column: type === "disable-next-line" ? parentComment.commentToken.loc.start.column + 1 : column,
+                line: type === "disable-next-line" ? loc.start.line + lineOffset : line,
+                column: type === "disable-next-line" ? loc.start.column + columnOffset : column,
                 severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,
                 nodeType: null,
@@ -399,4 +407,6 @@
  * of reported problems, adds the suppression information to the problems.
  * @param {Object} options Information about directives and problems
+ * @param {Language} options.language The language being linted.
+ * @param {SourceCode} options.sourceCode The source code object for the file being linted.
  * @param {{
  *      type: ("disable"|"enable"|"disable-line"|"disable-next-line"),
@@ -411,9 +421,11 @@
  * A list of problems reported by rules, sorted by increasing location in the file, with one-based columns.
  * @param {"off" | "warn" | "error"} options.reportUnusedDisableDirectives If `"warn"` or `"error"`, adds additional problems for unused directives
+ * @param {RulesConfig} options.configuredRules The rules configuration.
+ * @param {Function} options.ruleFilter A predicate function to filter which rules should be executed.
  * @param {boolean} options.disableFixes If true, it doesn't make `fix` properties.
  * @returns {{ruleId: (string|null), line: number, column: number, suppressions?: {kind: string, justification: string}}[]}
  * An object with a list of reported problems, the suppressed of which contain the suppression information.
  */
-module.exports = ({ directives, disableFixes, problems, reportUnusedDisableDirectives = "off" }) => {
+module.exports = ({ language, sourceCode, directives, disableFixes, problems, configuredRules, ruleFilter, reportUnusedDisableDirectives = "off" }) => {
     const blockDirectives = directives
         .filter(directive => directive.type === "disable" || directive.type === "enable")
@@ -444,15 +456,40 @@
     }).sort(compareLocations);
 
+    // This determines a list of rules that are not being run by the given ruleFilter, if present.
+    const rulesToIgnore = configuredRules && ruleFilter
+        ? new Set(Object.keys(configuredRules).filter(ruleId => {
+            const severity = ConfigOps.getRuleSeverity(configuredRules[ruleId]);
+
+            // Ignore for disabled rules.
+            if (severity === 0) {
+                return false;
+            }
+
+            return !ruleFilter({ severity, ruleId });
+        }))
+        : new Set();
+
+    // If no ruleId is supplied that means this directive is applied to all rules, so we can't determine if it's unused if any rules are filtered out.
+    if (rulesToIgnore.size > 0) {
+        rulesToIgnore.add(null);
+    }
+
     const blockDirectivesResult = applyDirectives({
+        language,
+        sourceCode,
         problems,
         directives: blockDirectives,
         disableFixes,
-        reportUnusedDisableDirectives
+        reportUnusedDisableDirectives,
+        rulesToIgnore
     });
     const lineDirectivesResult = applyDirectives({
+        language,
+        sourceCode,
         problems: blockDirectivesResult.problems,
         directives: lineDirectives,
         disableFixes,
-        reportUnusedDisableDirectives
+        reportUnusedDisableDirectives,
+        rulesToIgnore
     });
 
diff --git a/lib/rules/array-bracket-newline.js b/lib/rules/array-bracket-newline.js
index v8.57.0..v9.18.0 100644
--- a/lib/rules/array-bracket-newline.js
+++ b/lib/rules/array-bracket-newline.js
@@ -75,5 +75,5 @@
             let consistent = false;
             let multiline = false;
-            let minItems = 0;
+            let minItems;
 
             if (option) {
diff --git a/lib/rules/array-bracket-spacing.js b/lib/rules/array-bracket-spacing.js
index v8.57.0..v9.18.0 100644
--- a/lib/rules/array-bracket-spacing.js
+++ b/lib/rules/array-bracket-spacing.js
@@ -200,5 +200,5 @@
                 penultimate = sourceCode.getTokenBefore(last),
                 firstElement = node.elements[0],
-                lastElement = node.elements[node.elements.length - 1];
+                lastElement = node.elements.at(-1);
 
             const openingBracketMustBeSpaced =
diff --git a/lib/rules/array-callback-return.js b/lib/rules/array-callback-return.js
index v8.57.0..v9.18.0 100644
--- a/lib/rules/array-callback-return.js
+++ b/lib/rules/array-callback-return.js
@@ -216,4 +216,10 @@
         type: "problem",
 
+        defaultOptions: [{
+            allowImplicit: false,
+            checkForEach: false,
+            allowVoid: false
+        }],
+
         docs: {
             description: "Enforce `return` statements in callbacks of array methods",
@@ -230,14 +236,11 @@
                 properties: {
                     allowImplicit: {
-                        type: "boolean",
-                        default: false
+                        type: "boolean"
                     },
                     checkForEach: {
-                        type: "boolean",
-                        default: false
+                        type: "boolean"
                     },
                     allowVoid: {
-                        type: "boolean",
-                        default: false
+                        type: "boolean"
                     }
                 },
@@ -257,6 +260,5 @@
 
     create(context) {
-
-        const options = context.options[0] || { allowImplicit: false, checkForEach: false, allowVoid: false };
+        const [options] = context.options;
         const sourceCode = context.sourceCode;
 
diff --git a/lib/rules/arrow-body-style.js b/lib/rules/arrow-body-style.js
index v8.57.0..v9.18.0 100644
--- a/lib/rules/arrow-body-style.js
+++ b/lib/rules/arrow-body-style.js
@@ -20,7 +20,10 @@
         type: "suggestion",
 
+        defaultOptions: ["as-needed"],
+
         docs: {
             description: "Require braces around arrow function bodies",
             recommended: false,
+            frozen: true,
             url: "https://eslint.org/docs/latest/rules/arrow-body-style"
         },
@@ -72,5 +75,5 @@
         const options = context.options;
         const always = options[0] === "always";
-        const asNeeded = !options[0] || options[0] === "as-needed";
+        const asNeeded = options[0] === "as-needed";
         const never = options[0] === "never";
         const requireReturnForObjectLiteral = options[1] && options[1].requireReturnForObjectLiteral;
diff --git a/lib/rules/utils/ast-utils.js b/lib/rules/utils/ast-utils.js
index v8.57.0..v9.18.0 100644
--- a/lib/rules/utils/ast-utils.js
+++ b/lib/rules/utils/ast-utils.js
@@ -20,4 +20,6 @@
     shebangPattern
 } = require("../../shared/ast-utils");
+const globals = require("../../../conf/globals");
+const { LATEST_ECMA_VERSION } = require("../../../conf/ecma-version");
 
 //------------------------------------------------------------------------------
@@ -48,4 +50,10 @@
 
 /**
+ * All builtin global variables defined in the latest ECMAScript specification.
+ * @type {Record<string,boolean>} Key is the name of the variable. Value is `true` if the variable is considered writable, `false` otherwise.
+ */
+const ECMASCRIPT_GLOBALS = globals[`es${LATEST_ECMA_VERSION}`];
+
+/**
  * Checks reference if is non initializer and writable.
  * @param {Reference} reference A reference to check.
@@ -970,5 +978,5 @@
 
         case "SequenceExpression":
-            return isConstant(scope, node.expressions[node.expressions.length - 1], inBooleanPosition);
+            return isConstant(scope, node.expressions.at(-1), inBooleanPosition);
         case "SpreadElement":
             return isConstant(scope, node.argument, inBooleanPosition);
@@ -1035,9 +1043,10 @@
 /**
  * Determines whether an opening parenthesis `(`, bracket `[` or backtick ``` ` ``` needs to be preceded by a semicolon.
- * This opening parenthesis or bracket should be at the start of an `ExpressionStatement` or at the start of the body of an `ArrowFunctionExpression`.
+ * This opening parenthesis or bracket should be at the start of an `ExpressionStatement`, a `MethodDefinition` or at
+ * the start of the body of an `ArrowFunctionExpression`.
  * @type {(sourceCode: SourceCode, node: ASTNode) => boolean}
  * @param {SourceCode} sourceCode The source code object.
  * @param {ASTNode} node A node at the position where an opening parenthesis or bracket will be inserted.
- * @returns {boolean} Whether a semicolon is required before the opening parenthesis or braket.
+ * @returns {boolean} Whether a semicolon is required before the opening parenthesis or bracket.
  */
 let needsPrecedingSemicolon;
@@ -1046,5 +1055,5 @@
     const BREAK_OR_CONTINUE = new Set(["BreakStatement", "ContinueStatement"]);
 
-    // Declaration types that must contain a string Literal node at the end.
+    // Declaration types that cannot be continued by a punctuator when ending with a string Literal that is a direct child.
     const DECLARATIONS = new Set(["ExportAllDeclaration", "ExportNamedDeclaration", "ImportDeclaration"]);
 
@@ -1099,5 +1108,5 @@
         if (isClosingBraceToken(prevToken)) {
             return (
-                prevNode.type === "BlockStatement" && prevNode.parent.type === "FunctionExpression" ||
+                prevNode.type === "BlockStatement" && prevNode.parent.type === "FunctionExpression" && prevNode.parent.parent.type !== "MethodDefinition" ||
                 prevNode.type === "ClassBody" && prevNode.parent.type === "ClassExpression" ||
                 prevNode.type === "ObjectExpression"
@@ -1124,4 +1133,46 @@
 }
 
+/**
+ * Checks if a node is used as an import attribute key, either in a static or dynamic import.
+ * @param {ASTNode} node The node to check.
+ * @returns {boolean} Whether the node is used as an import attribute key.
+ */
+function isImportAttributeKey(node) {
+    const { parent } = node;
+
+    // static import/re-export
+    if (parent.type === "ImportAttribute" && parent.key === node) {
+        return true;
+    }
+
+    // dynamic import
+    if (
+        parent.type === "Property" &&
+        !parent.computed &&
+        (parent.key === node || parent.value === node && parent.shorthand && !parent.method) &&
+        parent.parent.type === "ObjectExpression"
+    ) {
+        const objectExpression = parent.parent;
+        const objectExpressionParent = objectExpression.parent;
+
+        if (
+            objectExpressionParent.type === "ImportExpression" &&
+            objectExpressionParent.options === objectExpression
+        ) {
+            return true;
+        }
+
+        // nested key
+        if (
+            objectExpressionParent.type === "Property" &&
+            objectExpressionParent.value === objectExpression
+        ) {
+            return isImportAttributeKey(objectExpressionParent.key);
+        }
+    }
+
+    return false;
+}
+
 //------------------------------------------------------------------------------
 // Public Interface
@@ -1134,4 +1185,5 @@
     SHEBANG_MATCHER: shebangPattern,
     STATEMENT_LIST_PARENTS,
+    ECMASCRIPT_GLOBALS,
 
     /**
@@ -1232,5 +1284,5 @@
      */
     isSurroundedBy(val, character) {
-        return val[0] === character && val[val.length - 1] === character;
+        return val[0] === character && val.at(-1) === character;
     },
 
@@ -1910,6 +1962,6 @@
     getFunctionHeadLoc(node, sourceCode) {
         const parent = node.parent;
-        let start = null;
-        let end = null;
+        let start;
+        let end;
 
         if (parent.type === "Property" || parent.type === "MethodDefinition" || parent.type === "PropertyDefinition") {
@@ -2056,5 +2108,5 @@
                 const exprs = node.expressions;
 
-                return exprs.length !== 0 && module.exports.couldBeError(exprs[exprs.length - 1]);
+                return exprs.length !== 0 && module.exports.couldBeError(exprs.at(-1));
             }
 
@@ -2120,7 +2172,7 @@
             const comments = tokens.comments;
 
-            leftToken = tokens[tokens.length - 1];
+            leftToken = tokens.at(-1);
             if (comments.length) {
-                const lastComment = comments[comments.length - 1];
+                const lastComment = comments.at(-1);
 
                 if (!leftToken || lastComment.range[0] > leftToken.range[0]) {
@@ -2260,4 +2312,145 @@
     },
 
+    /**
+     * Determines whether the existing curly braces around the single statement are necessary to preserve the semantics of the code.
+     * The braces, which make the given block body, are necessary in either of the following situations:
+     *
+     * 1. The statement is a lexical declaration.
+     * 2. Without the braces, an `if` within the statement would become associated with an `else` after the closing brace:
+     *
+     *     if (a) {
+     *         if (b)
+     *             foo();
+     *     }
+     *     else
+     *         bar();
+     *
+     *     if (a)
+     *         while (b)
+     *             while (c) {
+     *                 while (d)
+     *                     if (e)
+     *                         while(f)
+     *                             foo();
+     *            }
+     *     else
+     *         bar();
+     * @param {ASTNode} node `BlockStatement` body with exactly one statement directly inside. The statement can have its own nested statements.
+     * @param {SourceCode} sourceCode The source code
+     * @returns {boolean} `true` if the braces are necessary - removing them (replacing the given `BlockStatement` body with its single statement content)
+     * would change the semantics of the code or produce a syntax error.
+     */
+    areBracesNecessary(node, sourceCode) {
+
+        /**
+         * Determines if the given node is a lexical declaration (let, const, function, or class)
+         * @param {ASTNode} nodeToCheck The node to check
+         * @returns {boolean} True if the node is a lexical declaration
+         * @private
+         */
+        function isLexicalDeclaration(nodeToCheck) {
+            if (nodeToCheck.type === "VariableDeclaration") {
+                return nodeToCheck.kind === "const" || nodeToCheck.kind === "let";
+            }
+
+            return nodeToCheck.type === "FunctionDeclaration" || nodeToCheck.type === "ClassDeclaration";
+        }
+
+
+        /**
+         * Checks if the given token is an `else` token or not.
+         * @param {Token} token The token to check.
+         * @returns {boolean} `true` if the token is an `else` token.
+         */
+        function isElseKeywordToken(token) {
+            return token.value === "else" && token.type === "Keyword";
+        }
+
+        /**
+         * Determines whether the given node has an `else` keyword token as the first token after.
+         * @param {ASTNode} nodeToCheck The node to check.
+         * @returns {boolean} `true` if the node is followed by an `else` keyword token.
+         */
+        function isFollowedByElseKeyword(nodeToCheck) {
+            const nextToken = sourceCode.getTokenAfter(nodeToCheck);
+
+            return Boolean(nextToken) && isElseKeywordToken(nextToken);
+        }
+
+        /**
+         * Determines whether the code represented by the given node contains an `if` statement
+         * that would become associated with an `else` keyword directly appended to that code.
+         *
+         * Examples where it returns `true`:
+         *
+         *    if (a)
+         *        foo();
+         *
+         *    if (a) {
+         *        foo();
+         *    }
+         *
+         *    if (a)
+         *        foo();
+         *    else if (b)
+         *        bar();
+         *
+         *    while (a)
+         *        if (b)
+         *            if(c)
+         *                foo();
+         *            else
+         *                bar();
+         *
+         * Examples where it returns `false`:
+         *
+         *    if (a)
+         *        foo();
+         *    else
+         *        bar();
+         *
+         *    while (a) {
+         *        if (b)
+         *            if(c)
+         *                foo();
+         *            else
+         *                bar();
+         *    }
+         *
+         *    while (a)
+         *        if (b) {
+         *            if(c)
+         *                foo();
+         *        }
+         *        else
+         *            bar();
+         * @param {ASTNode} nodeToCheck Node representing the code to check.
+         * @returns {boolean} `true` if an `if` statement within the code would become associated with an `else` appended to that code.
+         */
+        function hasUnsafeIf(nodeToCheck) {
+            switch (nodeToCheck.type) {
+                case "IfStatement":
+                    if (!nodeToCheck.alternate) {
+                        return true;
+                    }
+                    return hasUnsafeIf(nodeToCheck.alternate);
+                case "ForStatement":
+                case "ForInStatement":
+                case "ForOfStatement":
+                case "LabeledStatement":
+                case "WithStatement":
+                case "WhileStatement":
+                    return hasUnsafeIf(nodeToCheck.body);
+                default:
+                    return false;
+            }
+        }
+
+        const statement = node.body[0];
+
+        return isLexicalDeclaration(statement) ||
+          hasUnsafeIf(statement) && isFollowedByElseKeyword(node);
+    },
+
     isReferenceToGlobalVariable,
     isLogicalExpression,
@@ -2279,4 +2472,5 @@
     isDirective,
     isStartOfExpressionStatement,
-    needsPrecedingSemicolon
+    needsPrecedingSemicolon,
+    isImportAttributeKey
 };
diff --git a/lib/source-code/token-store/backward-token-comment-cursor.js b/lib/source-code/token-store/backward-token-comment-cursor.js
deleted file mode 100644
index v8.57.0..v9.18.0 
--- a/lib/source-code/token-store/backward-token-comment-cursor.js
+++ b/lib/source-code/token-store/backward-token-comment-cursor.js
@@ -1,57 +0,0 @@
-/**
- * @fileoverview Define the cursor which iterates tokens and comments in reverse.
- * @author Toru Nagashima
- */
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const Cursor = require("./cursor");
-const utils = require("./utils");
-
-//------------------------------------------------------------------------------
-// Exports
-//------------------------------------------------------------------------------
-
-/**
- * The cursor which iterates tokens and comments in reverse.
- */
-module.exports = class BackwardTokenCommentCursor extends Cursor {
-
-    /**
-     * Initializes this cursor.
-     * @param {Token[]} tokens The array of tokens.
-     * @param {Comment[]} comments The array of comments.
-     * @param {Object} indexMap The map from locations to indices in `tokens`.
-     * @param {number} startLoc The start location of the iteration range.
-     * @param {number} endLoc The end location of the iteration range.
-     */
-    constructor(tokens, comments, indexMap, startLoc, endLoc) {
-        super();
-        this.tokens = tokens;
-        this.comments = comments;
-        this.tokenIndex = utils.getLastIndex(tokens, indexMap, endLoc);
-        this.commentIndex = utils.search(comments, endLoc) - 1;
-        this.border = startLoc;
-    }
-
-    /** @inheritdoc */
-    moveNext() {
-        const token = (this.tokenIndex >= 0) ? this.tokens[this.tokenIndex] : null;
-        const comment = (this.commentIndex >= 0) ? this.comments[this.commentIndex] : null;
-
-        if (token && (!comment || token.range[1] > comment.range[1])) {
-            this.current = token;
-            this.tokenIndex -= 1;
-        } else if (comment) {
-            this.current = comment;
-            this.commentIndex -= 1;
-        } else {
-            this.current = null;
-        }
-
-        return Boolean(this.current) && (this.border === -1 || this.current.range[0] >= this.border);
-    }
-};
diff --git a/lib/source-code/token-store/backward-token-cursor.js b/lib/source-code/token-store/backward-token-cursor.js
deleted file mode 100644
index v8.57.0..v9.18.0 
--- a/lib/source-code/token-store/backward-token-cursor.js
+++ b/lib/source-code/token-store/backward-token-cursor.js
@@ -1,58 +0,0 @@
-/**
- * @fileoverview Define the cursor which iterates tokens only in reverse.
- * @author Toru Nagashima
- */
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-const Cursor = require("./cursor");
-const utils = require("./utils");
-
-//------------------------------------------------------------------------------
-// Exports
-//------------------------------------------------------------------------------
-
-/**
- * The cursor which iterates tokens only in reverse.
- */
-module.exports = class BackwardTokenCursor extends Cursor {
-
-    /**
-     * Initializes this cursor.
-     * @param {Token[]} tokens The array of tokens.
-     * @param {Comment[]} comments The array of comments.
-     * @param {Object} indexMap The map from locations to indices in `tokens`.
-     * @param {number} startLoc The start location of the iteration range.
-     * @param {number} endLoc The end location of the iteration range.
-     */
-    constructor(tokens, comments, indexMap, startLoc, endLoc) {
-        super();
-        this.tokens = tokens;
-        this.index = utils.getLastIndex(tokens, indexMap, endLoc);
-        this.indexEnd = utils.getFirstIndex(tokens, indexMap, startLoc);
-    }
-
-    /** @inheritdoc */
-    moveNext() {
-        if (this.index >= this.indexEnd) {
-            this.current = this.tokens[this.index];
-            this.index -= 1;
-            return true;
-        }
-        return false;
-    }
-
-    /*
-     *
-     * Shorthand for performance.
-     *
-     */
-
-    /** @inheritdoc */
-    getOneToken() {
-        return (this.index >= this.indexEnd) ? this.tokens[this.index] : null;
-    }
-};
diff --git a/lib/rules/block-scoped-var.js b/lib/rules/block-scoped-var.js
index v8.57.0..v9.18.0 100644
--- a/lib/rules/block-scoped-var.js
+++ b/lib/rules/block-scoped-var.js
@@ -80,5 +80,5 @@
 
             // Defines a predicate to check whether or not a given reference is outside of valid scope.
-            const scopeRange = stack[stack.length - 1];
+            const scopeRange = stack.at(-1);
 
             /**
diff --git a/lib/rules/callback-return.js b/lib/rules/callback-return.js
index v8.57.0..v9.18.0 100644
--- a/lib/rules/callback-return.js
+++ b/lib/rules/callback-return.js
@@ -148,5 +148,5 @@
 
                     // find the last item in the block
-                    const lastItem = closestBlock.body[closestBlock.body.length - 1];
+                    const lastItem = closestBlock.body.at(-1);
 
                     // if the callback is the last thing in a block that might be ok
@@ -169,5 +169,5 @@
 
                         // but only if the callback is immediately before
-                        if (isCallbackExpression(node, closestBlock.body[closestBlock.body.length - 2])) {
+                        if (isCallbackExpression(node, closestBlock.body.at(-2))) {
                             return;
                         }
diff --git a/lib/rules/camelcase.js b/lib/rules/camelcase.js
index v8.57.0..v9.18.0 100644
--- a/lib/rules/camelcase.js
+++ b/lib/rules/camelcase.js
@@ -21,7 +21,16 @@
         type: "suggestion",
 
+        defaultOptions: [{
+            allow: [],
+            ignoreDestructuring: false,
+            ignoreGlobals: false,
+            ignoreImports: false,
+            properties: "always"
+        }],
+
         docs: {
             description: "Enforce camelcase naming convention",
             recommended: false,
+            frozen: true,
             url: "https://eslint.org/docs/latest/rules/camelcase"
         },
@@ -32,14 +41,11 @@
                 properties: {
                     ignoreDestructuring: {
-                        type: "boolean",
-                        default: false
+                        type: "boolean"
                     },
                     ignoreImports: {
-                        type: "boolean",
-                        default: false
+                        type: "boolean"
                     },
                     ignoreGlobals: {
-                        type: "boolean",
-                        default: false
+                        type: "boolean"
                     },
                     properties: {
@@ -48,9 +54,7 @@
                     allow: {
                         type: "array",
-                        items: [
-                            {
-                                type: "string"
-                            }
-                        ],
+                        items: {
+                            type: "string"
+                        },
                         minItems: 0,
                         uniqueItems: true
@@ -68,10 +72,11 @@
 
     create(context) {
-        const options = context.options[0] || {};
-        const properties = options.properties === "never" ? "never" : "always";
-        const ignoreDestructuring = options.ignoreDestructuring;
-        const ignoreImports = options.ignoreImports;
-        const ignoreGlobals = options.ignoreGlobals;
-        const allow = options.allow || [];
+        const [{
+            allow,
+            ignoreDestructuring,
+            ignoreGlobals,
+            ignoreImports,
+            properties
+        }] = context.options;
         const sourceCode = context.sourceCode;
 
@@ -241,4 +246,11 @@
             }
 
+            /*
+             * Import attribute keys are always ignored
+             */
+            if (astUtils.isImportAttributeKey(node)) {
+                return;
+            }
+
             report(node);
         }
@@ -275,5 +287,5 @@
                     const id = reference.identifier;
 
-                    if (isGoodName(id.name)) {
+                    if (isGoodName(id.name) || astUtils.isImportAttributeKey(id)) {
                         continue;
                     }
@@ -329,5 +341,5 @@
                 "PropertyDefinition > PrivateIdentifier.key"
             ]](node) {
-                if (properties === "never" || isGoodName(node.name)) {
+                if (properties === "never" || astUtils.isImportAttributeKey(node) || isGoodName(node.name)) {
                     return;
                 }
diff --git a/lib/rules/capitalized-comments.js b/lib/rules/capitalized-comments.js
index v8.57.0..v9.18.0 100644
--- a/lib/rules/capitalized-comments.js
+++ b/lib/rules/capitalized-comments.js
@@ -9,5 +9,4 @@
 //------------------------------------------------------------------------------
 
-const LETTER_PATTERN = require("./utils/patterns/letters");
 const astUtils = require("./utils/ast-utils");
 
@@ -18,5 +17,6 @@
 const DEFAULT_IGNORE_PATTERN = astUtils.COMMENTS_IGNORE_PATTERN,
     WHITESPACE = /\s/gu,
-    MAYBE_URL = /^\s*[^:/?#\s]+:\/\/[^?#]/u; // TODO: Combine w/ max-len pattern?
+    MAYBE_URL = /^\s*[^:/?#\s]+:\/\/[^?#]/u, // TODO: Combine w/ max-len pattern?
+    LETTER_PATTERN = /\p{L}/u;
 
 /*
@@ -108,4 +108,5 @@
             description: "Enforce or disallow capitalization of the first letter of a comment",
             recommended: false,
+            frozen: true,
             url: "https://eslint.org/docs/latest/rules/capitalized-comments"
         },
@@ -234,5 +235,6 @@
             }
 
-            const firstWordChar = commentWordCharsOnly[0];
+            // Get the first Unicode character (1 or 2 code units).
+            const [firstWordChar] = commentWordCharsOnly;
 
             if (!LETTER_PATTERN.test(firstWordChar)) {
@@ -274,10 +276,12 @@
                     fix(fixer) {
                         const match = comment.value.match(LETTER_PATTERN);
+                        const char = match[0];
 
-                        return fixer.replaceTextRange(
+                        // Offset match.index by 2 to account for the first 2 characters that start the comment (// or /*)
+                        const charIndex = comment.range[0] + match.index + 2;
 
-                            // Offset match.index by 2 to account for the first 2 characters that start the comment (// or /*)
-                            [comment.range[0] + match.index + 2, comment.range[0] + match.index + 3],
-                            capitalize === "always" ? match[0].toLocaleUpperCase() : match[0].toLocaleLowerCase()
+                        return fixer.replaceTextRange(
+                            [charIndex, charIndex + char.length],
+                            capitalize === "always" ? char.toLocaleUpperCase() : char.toLocaleLowerCase()
                         );
                     }
diff --git a/lib/cli-engine/formatters/checkstyle.js b/lib/cli-engine/formatters/checkstyle.js
deleted file mode 100644
index v8.57.0..v9.18.0 
--- a/lib/cli-engine/formatters/checkstyle.js
+++ b/lib/cli-engine/formatters/checkstyle.js
@@ -1,60 +0,0 @@
-/**
- * @fileoverview CheckStyle XML reporter
- * @author Ian Christian Myers
- */
-"use strict";
-
-const xmlEscape = require("../xml-escape");
-
-//------------------------------------------------------------------------------
-// Helper Functions
-//------------------------------------------------------------------------------
-
-/**
- * Returns the severity of warning or error
- * @param {Object} message message object to examine
- * @returns {string} severity level
- * @private
- */
-function getMessageType(message) {
-    if (message.fatal || message.severity === 2) {
-        return "error";
-    }
-    return "warning";
-
-}
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-module.exports = function(results) {
-
-    let output = "";
-
-    output += "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
-    output += "<checkstyle version=\"4.3\">";
-
-    results.forEach(result => {
-        const messages = result.messages;
-
-        output += `<file name="${xmlEscape(result.filePath)}">`;
-
-        messages.forEach(message => {
-            output += [
-                `<error line="${xmlEscape(message.line || 0)}"`,
-                `column="${xmlEscape(message.column || 0)}"`,
-                `severity="${xmlEscape(getMessageType(message))}"`,
-                `message="${xmlEscape(message.message)}${message.ruleId ? ` (${message.ruleId})` : ""}"`,
-                `source="${message.ruleId ? xmlEscape(`eslint.rules.${message.ruleId}`) : ""}" />`
-            ].join(" ");
-        });
-
-        output += "</file>";
-
-    });
-
-    output += "</checkstyle>";
-
-    return output;
-};
diff --git a/lib/rules/class-methods-use-this.js b/lib/rules/class-methods-use-this.js
index v8.57.0..v9.18.0 100644
--- a/lib/rules/class-methods-use-this.js
+++ b/lib/rules/class-methods-use-this.js
@@ -21,4 +21,9 @@
         type: "suggestion",
 
+        defaultOptions: [{
+            enforceForClassFields: true,
+            exceptMethods: []
+        }],
+
         docs: {
             description: "Enforce that class methods utilize `this`",
@@ -37,6 +42,5 @@
                 },
                 enforceForClassFields: {
-                    type: "boolean",
-                    default: true
+                    type: "boolean"
                 }
             },
@@ -49,7 +53,7 @@
     },
     create(context) {
-        const config = Object.assign({}, context.options[0]);
-        const enforceForClassFields = config.enforceForClassFields !== false;
-        const exceptMethods = new Set(config.exceptMethods || []);
+        const [options] = context.options;
+        const { enforceForClassFields } = options;
+        const exceptMethods = new Set(options.exceptMethods);
 
         const stack = [];
diff --git a/lib/cli-engine/cli-engine.js b/lib/cli-engine/cli-engine.js
index v8.57.0..v9.18.0 100644
--- a/lib/cli-engine/cli-engine.js
+++ b/lib/cli-engine/cli-engine.js
@@ -16,6 +16,6 @@
 //------------------------------------------------------------------------------
 
-const fs = require("fs");
-const path = require("path");
+const fs = require("node:fs");
+const path = require("node:path");
 const defaultOptions = require("../../conf/default-cli-options");
 const pkg = require("../../package.json");
@@ -42,4 +42,15 @@
 
 const debug = require("debug")("eslint:cli-engine");
+const removedFormatters = new Set([
+    "checkstyle",
+    "codeframe",
+    "compact",
+    "jslint-xml",
+    "junit",
+    "table",
+    "tap",
+    "unix",
+    "visualstudio"
+]);
 const validFixTypes = new Set(["directive", "problem", "suggestion", "layout"]);
 
@@ -640,5 +651,5 @@
         const lintResultCache =
             options.cache ? new LintResultCache(cacheFilePath, options.cacheStrategy) : null;
-        const linter = new Linter({ cwd: options.cwd });
+        const linter = new Linter({ cwd: options.cwd, configType: "eslintrc" });
 
         /** @type {ConfigArray[]} */
@@ -722,5 +733,5 @@
      */
     static outputFixes(report) {
-        report.results.filter(result => Object.prototype.hasOwnProperty.call(result, "output")).forEach(result => {
+        report.results.filter(result => Object.hasOwn(result, "output")).forEach(result => {
             fs.writeFileSync(result.filePath, result.output);
         });
@@ -1048,5 +1059,5 @@
                 return require(formatterPath);
             } catch (ex) {
-                if (format === "table" || format === "codeframe") {
+                if (removedFormatters.has(format)) {
                     ex.message = `The ${format} formatter is no longer part of core ESLint. Install it manually with \`npm install -D eslint-formatter-${format}\``;
                 } else {
diff --git a/lib/cli.js b/lib/cli.js
index v8.57.0..v9.18.0 100644
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -16,9 +16,9 @@
 //------------------------------------------------------------------------------
 
-const fs = require("fs"),
-    path = require("path"),
-    { promisify } = require("util"),
-    { ESLint } = require("./eslint"),
-    { FlatESLint, shouldUseFlatConfig } = require("./eslint/flat-eslint"),
+const fs = require("node:fs"),
+    path = require("node:path"),
+    { promisify } = require("node:util"),
+    { LegacyESLint } = require("./eslint"),
+    { ESLint, shouldUseFlatConfig, locateConfigFileToUse } = require("./eslint/eslint"),
     createCLIOptions = require("./options"),
     log = require("./shared/logging"),
@@ -27,5 +27,4 @@
 const { Legacy: { naming } } = require("@eslint/eslintrc");
 const { ModuleImporter } = require("@humanwhocodes/module-importer");
-
 const debug = require("debug")("eslint:cli");
 
@@ -38,4 +37,5 @@
 /** @typedef {import("./eslint/eslint").LintResult} LintResult */
 /** @typedef {import("./options").ParsedCLIOptions} ParsedCLIOptions */
+/** @typedef {import("./shared/types").Plugin} Plugin */
 /** @typedef {import("./shared/types").ResultsMeta} ResultsMeta */
 
@@ -49,4 +49,30 @@
 
 /**
+ * Loads plugins with the specified names.
+ * @param {{ "import": (name: string) => Promise<any> }} importer An object with an `import` method called once for each plugin.
+ * @param {string[]} pluginNames The names of the plugins to be loaded, with or without the "eslint-plugin-" prefix.
+ * @returns {Promise<Record<string, Plugin>>} A mapping of plugin short names to implementations.
+ */
+async function loadPlugins(importer, pluginNames) {
+    const plugins = {};
+
+    await Promise.all(pluginNames.map(async pluginName => {
+
+        const longName = naming.normalizePackageName(pluginName, "eslint-plugin");
+        const module = await importer.import(longName);
+
+        if (!("default" in module)) {
+            throw new Error(`"${longName}" cannot be used with the \`--plugin\` option because its default module does not provide a \`default\` export`);
+        }
+
+        const shortName = naming.getShorthandName(pluginName, "eslint-plugin");
+
+        plugins[shortName] = module.default;
+    }));
+
+    return plugins;
+}
+
+/**
  * Predicate function for whether or not to apply fixes in quiet mode.
  * If a message is a warning, do not apply a fix.
@@ -60,4 +86,14 @@
 
 /**
+ * Predicate function for whether or not to run a rule in quiet mode.
+ * If a rule is set to warning, do not run it.
+ * @param {{ ruleId: string; severity: number; }} rule The rule id and severity.
+ * @returns {boolean} True if the lint rule should run, false otherwise.
+ */
+function quietRuleFilter(rule) {
+    return rule.severity === 2;
+}
+
+/**
  * Translates the CLI options into the options expected by the ESLint constructor.
  * @param {ParsedCLIOptions} cliOptions The CLI options to translate.
@@ -81,4 +117,5 @@
     fixDryRun,
     fixType,
+    flag,
     global,
     ignore,
@@ -95,5 +132,8 @@
     rule,
     rulesdir,
-    warnIgnored
+    stats,
+    warnIgnored,
+    passOnNoPatterns,
+    maxWarnings
 }, configType) {
 
@@ -107,8 +147,8 @@
         }
 
-        let globals = {};
+        const languageOptions = {};
 
         if (global) {
-            globals = global.reduce((obj, name) => {
+            languageOptions.globals = global.reduce((obj, name) => {
                 if (name.endsWith(":true")) {
                     obj[name.slice(0, -5)] = "writable";
@@ -117,12 +157,17 @@
                 }
                 return obj;
-            }, globals);
+            }, {});
         }
 
+        if (parserOptions) {
+            languageOptions.parserOptions = parserOptions;
+        }
+
+        if (parser) {
+            languageOptions.parser = await importer.import(parser);
+        }
+
         overrideConfig = [{
-            languageOptions: {
-                globals,
-                parserOptions: parserOptions || {}
-            },
+            ...Object.keys(languageOptions).length > 0 ? { languageOptions } : {},
             rules: rule ? rule : {}
         }];
@@ -136,20 +181,6 @@
         }
 
-        if (parser) {
-            overrideConfig[0].languageOptions.parser = await importer.import(parser);
-        }
-
         if (plugin) {
-            const plugins = {};
-
-            for (const pluginName of plugin) {
-
-                const shortName = naming.getShorthandName(pluginName, "eslint-plugin");
-                const longName = naming.normalizePackageName(pluginName, "eslint-plugin");
-
-                plugins[shortName] = await importer.import(longName);
-            }
-
-            overrideConfig[0].plugins = plugins;
+            overrideConfig[0].plugins = await loadPlugins(importer, plugin);
         }
 
@@ -188,10 +219,19 @@
         ignore,
         overrideConfig,
-        overrideConfigFile
+        overrideConfigFile,
+        passOnNoPatterns
     };
 
     if (configType === "flat") {
         options.ignorePatterns = ignorePattern;
+        options.stats = stats;
         options.warnIgnored = warnIgnored;
+        options.flags = flag;
+
+        /*
+         * For performance reasons rules not marked as 'error' are filtered out in quiet mode. As maxWarnings
+         * requires rules set to 'warn' to be run, we only filter out 'warn' rules if maxWarnings is not specified.
+         */
+        options.ruleFilter = quiet && maxWarnings === -1 ? quietRuleFilter : () => true;
     } else {
         options.resolvePluginsRelativeTo = resolvePluginsRelativeTo;
@@ -267,23 +307,21 @@
     const output = await formatter.format(results, resultsMeta);
 
-    if (output) {
-        if (outputFile) {
-            const filePath = path.resolve(process.cwd(), outputFile);
+    if (outputFile) {
+        const filePath = path.resolve(process.cwd(), outputFile);
 
-            if (await isDirectory(filePath)) {
-                log.error("Cannot write to output file path, it is a directory: %s", outputFile);
-                return false;
-            }
+        if (await isDirectory(filePath)) {
+            log.error("Cannot write to output file path, it is a directory: %s", outputFile);
+            return false;
+        }
 
-            try {
-                await mkdir(path.dirname(filePath), { recursive: true });
-                await writeFile(filePath, output);
-            } catch (ex) {
-                log.error("There was a problem writing the output file:\n%s", ex);
-                return false;
-            }
-        } else {
-            log.info(output);
+        try {
+            await mkdir(path.dirname(filePath), { recursive: true });
+            await writeFile(filePath, output);
+        } catch (ex) {
+            log.error("There was a problem writing the output file:\n%s", ex);
+            return false;
         }
+    } else if (output) {
+        log.info(output);
     }
 
@@ -302,11 +340,27 @@
 
     /**
+     * Calculates the command string for the --inspect-config operation.
+     * @param {string} configFile The path to the config file to inspect.
+     * @returns {Promise<string>} The command string to execute.
+     */
+    async calculateInspectConfigFlags(configFile) {
+
+        // find the config file
+        const {
+            configFilePath,
+            basePath
+        } = await locateConfigFileToUse({ cwd: process.cwd(), configFile });
+
+        return ["--config", configFilePath, "--basePath", basePath];
+    },
+
+    /**
      * Executes the CLI based on an array of arguments that is passed in.
      * @param {string|Array|Object} args The arguments to process.
      * @param {string} [text] The text to lint (used for TTY).
-     * @param {boolean} [allowFlatConfig] Whether or not to allow flat config.
+     * @param {boolean} [allowFlatConfig=true] Whether or not to allow flat config.
      * @returns {Promise<number>} The exit code for the operation.
      */
-    async execute(args, text, allowFlatConfig) {
+    async execute(args, text, allowFlatConfig = true) {
         if (Array.isArray(args)) {
             debug("CLI args: %o", args.slice(2));
@@ -324,4 +378,8 @@
         debug("Using flat config?", usingFlatConfig);
 
+        if (allowFlatConfig && !usingFlatConfig) {
+            process.emitWarning("You are using an eslintrc configuration file, which is deprecated and support will be removed in v10.0.0. Please migrate to an eslint.config.js file. See https://eslint.org/docs/latest/use/configure/migration-guide for details. An eslintrc configuration file is used because you have the ESLINT_USE_FLAT_CONFIG environment variable set to false. If you want to use an eslint.config.js file, remove the environment variable. If you want to find the location of the eslintrc configuration file, use the --debug flag.", "ESLintRCWarning");
+        }
+
         const CLIOptions = createCLIOptions(usingFlatConfig);
 
@@ -377,6 +435,6 @@
 
             const engine = usingFlatConfig
-                ? new FlatESLint(await translateOptions(options, "flat"))
-                : new ESLint(await translateOptions(options));
+                ? new ESLint(await translateOptions(options, "flat"))
+                : new LegacyESLint(await translateOptions(options));
             const fileConfig =
                 await engine.calculateConfigForFile(options.printConfig);
@@ -386,4 +444,22 @@
         }
 
+        if (options.inspectConfig) {
+
+            log.info("You can also run this command directly using 'npx @eslint/config-inspector@latest' in the same directory as your configuration file.");
+
+            try {
+                const flatOptions = await translateOptions(options, "flat");
+                const spawn = require("cross-spawn");
+                const flags = await cli.calculateInspectConfigFlags(flatOptions.overrideConfigFile);
+
+                spawn.sync("npx", ["@eslint/config-inspector@latest", ...flags], { encoding: "utf8", stdio: "inherit" });
+            } catch (error) {
+                log.error(error);
+                return 2;
+            }
+
+            return 0;
+        }
+
         debug(`Running on ${useStdin ? "text" : "files"}`);
 
@@ -406,7 +482,7 @@
         }
 
-        const ActiveESLint = usingFlatConfig ? FlatESLint : ESLint;
-
-        const engine = new ActiveESLint(await translateOptions(options, usingFlatConfig ? "flat" : "eslintrc"));
+        const ActiveESLint = usingFlatConfig ? ESLint : LegacyESLint;
+        const eslintOptions = await translateOptions(options, usingFlatConfig ? "flat" : "eslintrc");
+        const engine = new ActiveESLint(eslintOptions);
         let results;
 
diff --git a/lib/linter/code-path-analysis/code-path-analyzer.js b/lib/linter/code-path-analysis/code-path-analyzer.js
index v8.57.0..v9.18.0 100644
--- a/lib/linter/code-path-analysis/code-path-analyzer.js
+++ b/lib/linter/code-path-analysis/code-path-analyzer.js
@@ -10,5 +10,5 @@
 //------------------------------------------------------------------------------
 
-const assert = require("assert"),
+const assert = require("../../shared/assert"),
     { breakableTypePattern } = require("../../shared/ast-utils"),
     CodePath = require("./code-path"),
@@ -223,5 +223,4 @@
 
             debug.dump(`${eventName} ${headSegment.id}`);
-
             CodePathSegment.markUsed(headSegment);
             analyzer.emitter.emit(
diff --git a/lib/linter/code-path-analysis/code-path.js b/lib/linter/code-path-analysis/code-path.js
index v8.57.0..v9.18.0 100644
--- a/lib/linter/code-path-analysis/code-path.js
+++ b/lib/linter/code-path-analysis/code-path.js
@@ -127,18 +127,4 @@
 
     /**
-     * Tracks the traversal of the code path through each segment. This array
-     * starts empty and segments are added or removed as the code path is
-     * traversed. This array always ends up empty at the end of a code path
-     * traversal. The `CodePathState` uses this to track its progress through
-     * the code path.
-     * This is a passthrough to the underlying `CodePathState`.
-     * @type {CodePathSegment[]}
-     * @deprecated
-     */
-    get currentSegments() {
-        return this.internal.currentSegments;
-    }
-
-    /**
      * Traverses all segments in this code path.
      *
@@ -181,7 +167,7 @@
 
         // set up initial location information
-        let record = null;
-        let index = 0;
-        let end = 0;
+        let record;
+        let index;
+        let end;
         let segment = null;
 
@@ -192,6 +178,6 @@
         const stack = [[startSegment, 0]];
 
-        // tracks the last skipped segment during traversal
-        let skippedSegment = null;
+        // segments that have been skipped during traversal
+        const skipped = new Set();
 
         // indicates if we exited early from the traversal
@@ -208,9 +194,5 @@
              */
             skip() {
-                if (stack.length <= 1) {
-                    broken = true;
-                } else {
-                    skippedSegment = stack[stack.length - 2][0];
-                }
+                skipped.add(segment);
             },
 
@@ -237,4 +219,16 @@
         }
 
+        /**
+         * Checks if a given previous segment has been skipped.
+         * @param {CodePathSegment} prevSegment A previous segment to check.
+         * @returns {boolean} `true` if the segment has been skipped.
+         */
+        function isSkipped(prevSegment) {
+            return (
+                skipped.has(prevSegment) ||
+                segment.isLoopedPrevSegment(prevSegment)
+            );
+        }
+
         // the traversal
         while (stack.length > 0) {
@@ -252,5 +246,5 @@
              * record as we traverse.
              */
-            record = stack[stack.length - 1];
+            record = stack.at(-1);
             segment = record[0];
             index = record[1];
@@ -273,15 +267,19 @@
                 }
 
-                // Reset the skipping flag if all branches have been skipped.
-                if (skippedSegment && segment.prevSegments.includes(skippedSegment)) {
-                    skippedSegment = null;
-                }
                 visited.add(segment);
 
+
+                // Skips the segment if all previous segments have been skipped.
+                const shouldSkip = (
+                    skipped.size > 0 &&
+                    segment.prevSegments.length > 0 &&
+                    segment.prevSegments.every(isSkipped)
+                );
+
                 /*
                  * If the most recent segment hasn't been skipped, then we call
                  * the callback, passing in the segment and the controller.
                  */
-                if (!skippedSegment) {
+                if (!shouldSkip) {
                     resolvedCallback.call(this, segment, controller);
 
@@ -299,4 +297,8 @@
                         break;
                     }
+                } else {
+
+                    // If the most recent segment has been skipped, then mark it as skipped.
+                    skipped.add(segment);
                 }
             }
diff --git a/lib/rules/comma-dangle.js b/lib/rules/comma-dangle.js
index v8.57.0..v9.18.0 100644
--- a/lib/rules/comma-dangle.js
+++ b/lib/rules/comma-dangle.js
@@ -155,5 +155,5 @@
              */
             function last(array) {
-                return array[array.length - 1];
+                return array.at(-1);
             }
 
diff --git a/lib/rules/comma-style.js b/lib/rules/comma-style.js
index v8.57.0..v9.18.0 100644
--- a/lib/rules/comma-style.js
+++ b/lib/rules/comma-style.js
@@ -67,5 +67,5 @@
         };
 
-        if (context.options.length === 2 && Object.prototype.hasOwnProperty.call(context.options[1], "exceptions")) {
+        if (context.options.length === 2 && Object.hasOwn(context.options[1], "exceptions")) {
             const keys = Object.keys(context.options[1].exceptions);
 
@@ -219,5 +219,5 @@
                         previousItemToken = token

(too long so truncated)

Size Files
2.9 MB → 3.2 MB (+326.0 KB 🟡) 408 → 423 (+15 🟡)
Command details
npm diff [email protected] [email protected] --diff-unified=2

See also the npm diff document.

Reported by ybiquitous/[email protected] (Node.js 22.12.0 and npm 11.0.0)

@dependabot dependabot bot force-pushed the dependabot/npm_and_yarn/eslint-9.18.0 branch 4 times, most recently from cd59d4e to 9e13849 Compare January 20, 2025 02:21
Bumps [eslint](https://github.com/eslint/eslint) from 8.57.0 to 9.18.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](eslint/eslint@v8.57.0...v9.18.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
@dependabot dependabot bot force-pushed the dependabot/npm_and_yarn/eslint-9.18.0 branch from 9e13849 to 935c1a0 Compare January 20, 2025 02:24
Copy link
Contributor Author

dependabot bot commented on behalf of github Jan 27, 2025

Superseded by #1844.

@dependabot dependabot bot closed this Jan 27, 2025
@dependabot dependabot bot deleted the dependabot/npm_and_yarn/eslint-9.18.0 branch January 27, 2025 02:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file javascript Pull requests that update Javascript code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

0 participants