eslint 9.26.0 → 9.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +7 -2
  2. package/bin/eslint.js +7 -11
  3. package/conf/rule-type-list.json +2 -1
  4. package/lib/cli-engine/cli-engine.js +7 -7
  5. package/lib/cli.js +19 -16
  6. package/lib/config/config-loader.js +42 -39
  7. package/lib/config/config.js +362 -16
  8. package/lib/eslint/eslint-helpers.js +3 -1
  9. package/lib/eslint/eslint.js +31 -13
  10. package/lib/eslint/legacy-eslint.js +6 -6
  11. package/lib/languages/js/source-code/source-code.js +40 -6
  12. package/lib/linter/apply-disable-directives.js +1 -1
  13. package/lib/linter/file-context.js +11 -0
  14. package/lib/linter/linter.js +102 -140
  15. package/lib/linter/report-translator.js +2 -1
  16. package/lib/linter/{node-event-generator.js → source-code-traverser.js} +143 -87
  17. package/lib/options.js +7 -0
  18. package/lib/rule-tester/rule-tester.js +3 -3
  19. package/lib/rules/func-style.js +57 -7
  20. package/lib/rules/index.js +1 -0
  21. package/lib/rules/max-params.js +32 -7
  22. package/lib/rules/no-array-constructor.js +51 -1
  23. package/lib/rules/no-implicit-globals.js +31 -15
  24. package/lib/rules/no-magic-numbers.js +98 -5
  25. package/lib/rules/no-shadow.js +262 -6
  26. package/lib/rules/no-unassigned-vars.js +80 -0
  27. package/lib/rules/no-use-before-define.js +97 -1
  28. package/lib/rules/no-useless-escape.js +24 -2
  29. package/lib/rules/prefer-arrow-callback.js +9 -0
  30. package/lib/rules/prefer-named-capture-group.js +7 -1
  31. package/lib/services/processor-service.js +1 -1
  32. package/lib/services/suppressions-service.js +5 -3
  33. package/lib/services/warning-service.js +85 -0
  34. package/lib/shared/flags.js +1 -0
  35. package/lib/types/index.d.ts +132 -9
  36. package/lib/types/rules.d.ts +66 -3
  37. package/package.json +11 -11
  38. package/lib/config/flat-config-helpers.js +0 -128
  39. package/lib/config/rule-validator.js +0 -199
  40. package/lib/mcp/mcp-server.js +0 -66
  41. package/lib/shared/types.js +0 -229
@@ -619,7 +619,7 @@ export interface ESLintRules extends Linter.RulesRecord {
619
619
  * @see https://eslint.org/docs/latest/rules/curly
620
620
  */
621
621
  curly: Linter.RuleEntry<
622
- ["all" | "multi" | "multi-line" | "multi-or-nest" | "consistent"]
622
+ ["all"] | ["multi" | "multi-line" | "multi-or-nest", "consistent"?]
623
623
  >;
624
624
 
625
625
  /**
@@ -807,6 +807,10 @@ export interface ESLintRules extends Linter.RulesRecord {
807
807
  * @default false
808
808
  */
809
809
  allowArrowFunctions: boolean;
810
+ /**
811
+ * @default false
812
+ */
813
+ allowTypeAnnotation: boolean;
810
814
  overrides: {
811
815
  namedExports: "declaration" | "expression" | "ignore";
812
816
  };
@@ -1800,6 +1804,10 @@ export interface ESLintRules extends Linter.RulesRecord {
1800
1804
  * @default 3
1801
1805
  */
1802
1806
  max: number;
1807
+ /**
1808
+ * @default false
1809
+ */
1810
+ countVoidThis: boolean;
1803
1811
  }>
1804
1812
  | number,
1805
1813
  ]
@@ -2850,6 +2858,22 @@ export interface ESLintRules extends Linter.RulesRecord {
2850
2858
  * @default false
2851
2859
  */
2852
2860
  detectObjects: boolean;
2861
+ /**
2862
+ * @default false
2863
+ */
2864
+ ignoreEnums: boolean;
2865
+ /**
2866
+ * @default false
2867
+ */
2868
+ ignoreNumericLiteralTypes: boolean;
2869
+ /**
2870
+ * @default false
2871
+ */
2872
+ ignoreReadonlyClassProperties: boolean;
2873
+ /**
2874
+ * @default false
2875
+ */
2876
+ ignoreTypeIndexes: boolean;
2853
2877
  }>,
2854
2878
  ]
2855
2879
  >;
@@ -3558,13 +3582,26 @@ export interface ESLintRules extends Linter.RulesRecord {
3558
3582
  /**
3559
3583
  * @default 'functions'
3560
3584
  */
3561
- hoist: "functions" | "all" | "never";
3585
+ hoist:
3586
+ | "functions"
3587
+ | "all"
3588
+ | "never"
3589
+ | "types"
3590
+ | "functions-and-types";
3562
3591
  allow: string[];
3563
3592
  /**
3564
3593
  * @since 8.10.0
3565
3594
  * @default false
3566
3595
  */
3567
3596
  ignoreOnInitialization: boolean;
3597
+ /**
3598
+ * @default true
3599
+ */
3600
+ ignoreTypeValueShadow: boolean;
3601
+ /**
3602
+ * @default true
3603
+ */
3604
+ ignoreFunctionTypeParameterNameValueShadow: boolean;
3568
3605
  }>,
3569
3606
  ]
3570
3607
  >;
@@ -3710,6 +3747,14 @@ export interface ESLintRules extends Linter.RulesRecord {
3710
3747
  ]
3711
3748
  >;
3712
3749
 
3750
+ /**
3751
+ * Rule to disallow `let` or `var` variables that are read but never assigned.
3752
+ *
3753
+ * @since 9.27.0
3754
+ * @see https://eslint.org/docs/latest/rules/no-unassigned-vars
3755
+ */
3756
+ "no-unassigned-vars": Linter.RuleEntry<[]>;
3757
+
3713
3758
  /**
3714
3759
  * Rule to disallow the use of undeclared variables unless mentioned in \/*global *\/ comments.
3715
3760
  *
@@ -4045,6 +4090,18 @@ export interface ESLintRules extends Linter.RulesRecord {
4045
4090
  * @default false
4046
4091
  */
4047
4092
  allowNamedExports: boolean;
4093
+ /**
4094
+ * @default true
4095
+ */
4096
+ enums: boolean;
4097
+ /**
4098
+ * @default true
4099
+ */
4100
+ typedefs: boolean;
4101
+ /**
4102
+ * @default true
4103
+ */
4104
+ ignoreTypeReferences: boolean;
4048
4105
  }>
4049
4106
  | "nofunc",
4050
4107
  ]
@@ -4130,7 +4187,13 @@ export interface ESLintRules extends Linter.RulesRecord {
4130
4187
  * @since 2.5.0
4131
4188
  * @see https://eslint.org/docs/latest/rules/no-useless-escape
4132
4189
  */
4133
- "no-useless-escape": Linter.RuleEntry<[]>;
4190
+ "no-useless-escape": Linter.RuleEntry<
4191
+ [
4192
+ Partial<{
4193
+ allowRegexCharacters: string[];
4194
+ }>,
4195
+ ]
4196
+ >;
4134
4197
 
4135
4198
  /**
4136
4199
  * Rule to disallow renaming import, export, and destructured assignments to the same name.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "9.26.0",
3
+ "version": "9.28.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "type": "commonjs",
@@ -108,14 +108,13 @@
108
108
  "@eslint-community/regexpp": "^4.12.1",
109
109
  "@eslint/config-array": "^0.20.0",
110
110
  "@eslint/config-helpers": "^0.2.1",
111
- "@eslint/core": "^0.13.0",
111
+ "@eslint/core": "^0.14.0",
112
112
  "@eslint/eslintrc": "^3.3.1",
113
- "@eslint/js": "9.26.0",
114
- "@eslint/plugin-kit": "^0.2.8",
113
+ "@eslint/js": "9.28.0",
114
+ "@eslint/plugin-kit": "^0.3.1",
115
115
  "@humanfs/node": "^0.16.6",
116
116
  "@humanwhocodes/module-importer": "^1.0.1",
117
117
  "@humanwhocodes/retry": "^0.4.2",
118
- "@modelcontextprotocol/sdk": "^1.8.0",
119
118
  "@types/estree": "^1.0.6",
120
119
  "@types/json-schema": "^7.0.15",
121
120
  "ajv": "^6.12.4",
@@ -139,11 +138,10 @@
139
138
  "lodash.merge": "^4.6.2",
140
139
  "minimatch": "^3.1.2",
141
140
  "natural-compare": "^1.4.0",
142
- "optionator": "^0.9.3",
143
- "zod": "^3.24.2"
141
+ "optionator": "^0.9.3"
144
142
  },
145
143
  "devDependencies": {
146
- "@arethetypeswrong/cli": "^0.17.0",
144
+ "@arethetypeswrong/cli": "^0.18.0",
147
145
  "@babel/core": "^7.4.3",
148
146
  "@babel/preset-env": "^7.4.3",
149
147
  "@cypress/webpack-preprocessor": "^6.0.2",
@@ -172,10 +170,12 @@
172
170
  "fast-glob": "^3.2.11",
173
171
  "fs-teardown": "^0.1.3",
174
172
  "glob": "^10.0.0",
175
- "globals": "^15.0.0",
173
+ "globals": "^16.2.0",
176
174
  "got": "^11.8.3",
177
175
  "gray-matter": "^4.0.3",
178
- "jiti": "^2.1.0",
176
+ "jiti": "^2.2.0",
177
+ "jiti-v2.0": "npm:jiti@2.0.x",
178
+ "jiti-v2.1": "npm:jiti@2.1.x",
179
179
  "knip": "^5.32.0",
180
180
  "lint-staged": "^11.0.0",
181
181
  "load-perf": "^0.2.0",
@@ -197,7 +197,7 @@
197
197
  "recast": "^0.23.0",
198
198
  "regenerator-runtime": "^0.14.0",
199
199
  "semver": "^7.5.3",
200
- "shelljs": "^0.9.0",
200
+ "shelljs": "^0.10.0",
201
201
  "sinon": "^11.0.0",
202
202
  "typescript": "^5.3.3",
203
203
  "webpack": "^5.23.0",
@@ -1,128 +0,0 @@
1
- /**
2
- * @fileoverview Shared functions to work with configs.
3
- * @author Nicholas C. Zakas
4
- */
5
-
6
- "use strict";
7
-
8
- //------------------------------------------------------------------------------
9
- // Typedefs
10
- //------------------------------------------------------------------------------
11
-
12
- /**
13
- * @import { RuleDefinition } from "@eslint/core";
14
- * @import { Linter } from "eslint";
15
- */
16
-
17
- //------------------------------------------------------------------------------
18
- // Private Members
19
- //------------------------------------------------------------------------------
20
-
21
- // JSON schema that disallows passing any options
22
- const noOptionsSchema = Object.freeze({
23
- type: "array",
24
- minItems: 0,
25
- maxItems: 0,
26
- });
27
-
28
- //-----------------------------------------------------------------------------
29
- // Functions
30
- //-----------------------------------------------------------------------------
31
-
32
- /**
33
- * Parses a ruleId into its plugin and rule parts.
34
- * @param {string} ruleId The rule ID to parse.
35
- * @returns {{pluginName:string,ruleName:string}} The plugin and rule
36
- * parts of the ruleId;
37
- */
38
- function parseRuleId(ruleId) {
39
- let pluginName, ruleName;
40
-
41
- // distinguish between core rules and plugin rules
42
- if (ruleId.includes("/")) {
43
- // mimic scoped npm packages
44
- if (ruleId.startsWith("@")) {
45
- pluginName = ruleId.slice(0, ruleId.lastIndexOf("/"));
46
- } else {
47
- pluginName = ruleId.slice(0, ruleId.indexOf("/"));
48
- }
49
-
50
- ruleName = ruleId.slice(pluginName.length + 1);
51
- } else {
52
- pluginName = "@";
53
- ruleName = ruleId;
54
- }
55
-
56
- return {
57
- pluginName,
58
- ruleName,
59
- };
60
- }
61
-
62
- /**
63
- * Retrieves a rule instance from a given config based on the ruleId.
64
- * @param {string} ruleId The rule ID to look for.
65
- * @param {Linter.Config} config The config to search.
66
- * @returns {RuleDefinition|undefined} The rule if found
67
- * or undefined if not.
68
- */
69
- function getRuleFromConfig(ruleId, config) {
70
- const { pluginName, ruleName } = parseRuleId(ruleId);
71
-
72
- return config.plugins?.[pluginName]?.rules?.[ruleName];
73
- }
74
-
75
- /**
76
- * Gets a complete options schema for a rule.
77
- * @param {RuleDefinition} rule A rule object
78
- * @throws {TypeError} If `meta.schema` is specified but is not an array, object or `false`.
79
- * @returns {Object|null} JSON Schema for the rule's options. `null` if `meta.schema` is `false`.
80
- */
81
- function getRuleOptionsSchema(rule) {
82
- if (!rule.meta) {
83
- return { ...noOptionsSchema }; // default if `meta.schema` is not specified
84
- }
85
-
86
- const schema = rule.meta.schema;
87
-
88
- if (typeof schema === "undefined") {
89
- return { ...noOptionsSchema }; // default if `meta.schema` is not specified
90
- }
91
-
92
- // `schema:false` is an allowed explicit opt-out of options validation for the rule
93
- if (schema === false) {
94
- return null;
95
- }
96
-
97
- if (typeof schema !== "object" || schema === null) {
98
- throw new TypeError("Rule's `meta.schema` must be an array or object");
99
- }
100
-
101
- // ESLint-specific array form needs to be converted into a valid JSON Schema definition
102
- if (Array.isArray(schema)) {
103
- if (schema.length) {
104
- return {
105
- type: "array",
106
- items: schema,
107
- minItems: 0,
108
- maxItems: schema.length,
109
- };
110
- }
111
-
112
- // `schema:[]` is an explicit way to specify that the rule does not accept any options
113
- return { ...noOptionsSchema };
114
- }
115
-
116
- // `schema:<object>` is assumed to be a valid JSON Schema definition
117
- return schema;
118
- }
119
-
120
- //-----------------------------------------------------------------------------
121
- // Exports
122
- //-----------------------------------------------------------------------------
123
-
124
- module.exports = {
125
- parseRuleId,
126
- getRuleFromConfig,
127
- getRuleOptionsSchema,
128
- };
@@ -1,199 +0,0 @@
1
- /**
2
- * @fileoverview Rule Validator
3
- * @author Nicholas C. Zakas
4
- */
5
-
6
- "use strict";
7
-
8
- //-----------------------------------------------------------------------------
9
- // Requirements
10
- //-----------------------------------------------------------------------------
11
-
12
- const ajvImport = require("../shared/ajv");
13
- const ajv = ajvImport();
14
- const {
15
- parseRuleId,
16
- getRuleFromConfig,
17
- getRuleOptionsSchema,
18
- } = require("./flat-config-helpers");
19
- const ruleReplacements = require("../../conf/replacements.json");
20
-
21
- //-----------------------------------------------------------------------------
22
- // Helpers
23
- //-----------------------------------------------------------------------------
24
-
25
- /**
26
- * Throws a helpful error when a rule cannot be found.
27
- * @param {Object} ruleId The rule identifier.
28
- * @param {string} ruleId.pluginName The ID of the rule to find.
29
- * @param {string} ruleId.ruleName The ID of the rule to find.
30
- * @param {Object} config The config to search in.
31
- * @throws {TypeError} For missing plugin or rule.
32
- * @returns {void}
33
- */
34
- function throwRuleNotFoundError({ pluginName, ruleName }, config) {
35
- const ruleId = pluginName === "@" ? ruleName : `${pluginName}/${ruleName}`;
36
-
37
- const errorMessageHeader = `Key "rules": Key "${ruleId}"`;
38
-
39
- let errorMessage = `${errorMessageHeader}: Could not find plugin "${pluginName}" in configuration.`;
40
-
41
- const missingPluginErrorMessage = errorMessage;
42
-
43
- // if the plugin exists then we need to check if the rule exists
44
- if (config.plugins && config.plugins[pluginName]) {
45
- const replacementRuleName = ruleReplacements.rules[ruleName];
46
-
47
- if (pluginName === "@" && replacementRuleName) {
48
- errorMessage = `${errorMessageHeader}: Rule "${ruleName}" was removed and replaced by "${replacementRuleName}".`;
49
- } else {
50
- errorMessage = `${errorMessageHeader}: Could not find "${ruleName}" in plugin "${pluginName}".`;
51
-
52
- // otherwise, let's see if we can find the rule name elsewhere
53
- for (const [otherPluginName, otherPlugin] of Object.entries(
54
- config.plugins,
55
- )) {
56
- if (otherPlugin.rules && otherPlugin.rules[ruleName]) {
57
- errorMessage += ` Did you mean "${otherPluginName}/${ruleName}"?`;
58
- break;
59
- }
60
- }
61
- }
62
-
63
- // falls through to throw error
64
- }
65
-
66
- const error = new TypeError(errorMessage);
67
-
68
- if (errorMessage === missingPluginErrorMessage) {
69
- error.messageTemplate = "config-plugin-missing";
70
- error.messageData = { pluginName, ruleId };
71
- }
72
-
73
- throw error;
74
- }
75
-
76
- /**
77
- * The error type when a rule has an invalid `meta.schema`.
78
- */
79
- class InvalidRuleOptionsSchemaError extends Error {
80
- /**
81
- * Creates a new instance.
82
- * @param {string} ruleId Id of the rule that has an invalid `meta.schema`.
83
- * @param {Error} processingError Error caught while processing the `meta.schema`.
84
- */
85
- constructor(ruleId, processingError) {
86
- super(
87
- `Error while processing options validation schema of rule '${ruleId}': ${processingError.message}`,
88
- { cause: processingError },
89
- );
90
- this.code = "ESLINT_INVALID_RULE_OPTIONS_SCHEMA";
91
- }
92
- }
93
-
94
- //-----------------------------------------------------------------------------
95
- // Exports
96
- //-----------------------------------------------------------------------------
97
-
98
- /**
99
- * Implements validation functionality for the rules portion of a config.
100
- */
101
- class RuleValidator {
102
- /**
103
- * Creates a new instance.
104
- */
105
- constructor() {
106
- /**
107
- * A collection of compiled validators for rules that have already
108
- * been validated.
109
- * @type {WeakMap}
110
- */
111
- this.validators = new WeakMap();
112
- }
113
-
114
- /**
115
- * Validates all of the rule configurations in a config against each
116
- * rule's schema.
117
- * @param {Object} config The full config to validate. This object must
118
- * contain both the rules section and the plugins section.
119
- * @returns {void}
120
- * @throws {Error} If a rule's configuration does not match its schema.
121
- */
122
- validate(config) {
123
- if (!config.rules) {
124
- return;
125
- }
126
-
127
- for (const [ruleId, ruleOptions] of Object.entries(config.rules)) {
128
- // check for edge case
129
- if (ruleId === "__proto__") {
130
- continue;
131
- }
132
-
133
- /*
134
- * If a rule is disabled, we don't do any validation. This allows
135
- * users to safely set any value to 0 or "off" without worrying
136
- * that it will cause a validation error.
137
- *
138
- * Note: ruleOptions is always an array at this point because
139
- * this validation occurs after FlatConfigArray has merged and
140
- * normalized values.
141
- */
142
- if (ruleOptions[0] === 0) {
143
- continue;
144
- }
145
-
146
- const rule = getRuleFromConfig(ruleId, config);
147
-
148
- if (!rule) {
149
- throwRuleNotFoundError(parseRuleId(ruleId), config);
150
- }
151
-
152
- // Precompile and cache validator the first time
153
- if (!this.validators.has(rule)) {
154
- try {
155
- const schema = getRuleOptionsSchema(rule);
156
-
157
- if (schema) {
158
- this.validators.set(rule, ajv.compile(schema));
159
- }
160
- } catch (err) {
161
- throw new InvalidRuleOptionsSchemaError(ruleId, err);
162
- }
163
- }
164
-
165
- const validateRule = this.validators.get(rule);
166
-
167
- if (validateRule) {
168
- validateRule(ruleOptions.slice(1));
169
-
170
- if (validateRule.errors) {
171
- throw new Error(
172
- `Key "rules": Key "${ruleId}":\n${validateRule.errors
173
- .map(error => {
174
- if (
175
- error.keyword === "additionalProperties" &&
176
- error.schema === false &&
177
- typeof error.parentSchema?.properties ===
178
- "object" &&
179
- typeof error.params?.additionalProperty ===
180
- "string"
181
- ) {
182
- const expectedProperties = Object.keys(
183
- error.parentSchema.properties,
184
- ).map(property => `"${property}"`);
185
-
186
- return `\tValue ${JSON.stringify(error.data)} ${error.message}.\n\t\tUnexpected property "${error.params.additionalProperty}". Expected properties: ${expectedProperties.join(", ")}.\n`;
187
- }
188
-
189
- return `\tValue ${JSON.stringify(error.data)} ${error.message}.\n`;
190
- })
191
- .join("")}`,
192
- );
193
- }
194
- }
195
- }
196
- }
197
- }
198
-
199
- exports.RuleValidator = RuleValidator;
@@ -1,66 +0,0 @@
1
- /**
2
- * @fileoverview MCP Server for handling requests and responses to ESLint.
3
- * @author Nicholas C. Zakas
4
- */
5
-
6
- "use strict";
7
-
8
- //-----------------------------------------------------------------------------
9
- // Requirements
10
- //-----------------------------------------------------------------------------
11
-
12
- const { McpServer } = require("@modelcontextprotocol/sdk/server/mcp.js");
13
- const { z } = require("zod");
14
- const { ESLint } = require("../eslint");
15
- const pkg = require("../../package.json");
16
-
17
- //-----------------------------------------------------------------------------
18
- // Server
19
- //-----------------------------------------------------------------------------
20
-
21
- const mcpServer = new McpServer({
22
- name: "ESLint",
23
- version: pkg.version,
24
- });
25
-
26
- // Important: Cursor throws an error when `describe()` is used in the schema.
27
- const filePathsSchema = {
28
- filePaths: z.array(z.string().min(1)).nonempty(),
29
- };
30
-
31
- //-----------------------------------------------------------------------------
32
- // Tools
33
- //-----------------------------------------------------------------------------
34
-
35
- mcpServer.tool(
36
- "lint-files",
37
- "Lint files using ESLint. You must provide a list of absolute file paths to the files you want to lint. The absolute file paths should be in the correct format for your operating system (e.g., forward slashes on Unix-like systems, backslashes on Windows).",
38
- filePathsSchema,
39
- async ({ filePaths }) => {
40
- const eslint = new ESLint({
41
- // enable lookup from file rather than from cwd
42
- flags: ["unstable_config_lookup_from_file"],
43
- });
44
-
45
- const results = await eslint.lintFiles(filePaths);
46
- const content = results.map(result => ({
47
- type: "text",
48
- text: JSON.stringify(result),
49
- }));
50
-
51
- content.unshift({
52
- type: "text",
53
- text: "Here are the results of running ESLint on the provided files:",
54
- });
55
- content.push({
56
- type: "text",
57
- text: "Do not automatically fix these issues. You must ask the user for confirmation before attempting to fix the issues found.",
58
- });
59
-
60
- return {
61
- content,
62
- };
63
- },
64
- );
65
-
66
- module.exports = { mcpServer };