eslint 9.25.1 → 9.27.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 (40) hide show
  1. package/README.md +44 -39
  2. package/bin/eslint.js +15 -0
  3. package/conf/rule-type-list.json +2 -1
  4. package/lib/cli-engine/cli-engine.js +8 -8
  5. package/lib/cli.js +6 -5
  6. package/lib/config/config-loader.js +10 -18
  7. package/lib/config/config.js +328 -5
  8. package/lib/eslint/eslint-helpers.js +3 -1
  9. package/lib/eslint/eslint.js +31 -17
  10. package/lib/eslint/legacy-eslint.js +7 -7
  11. package/lib/languages/js/index.js +4 -3
  12. package/lib/languages/js/source-code/source-code.js +10 -6
  13. package/lib/linter/apply-disable-directives.js +1 -1
  14. package/lib/linter/esquery.js +329 -0
  15. package/lib/linter/file-context.js +11 -0
  16. package/lib/linter/linter.js +81 -89
  17. package/lib/linter/node-event-generator.js +94 -251
  18. package/lib/linter/report-translator.js +2 -1
  19. package/lib/options.js +11 -0
  20. package/lib/rule-tester/rule-tester.js +17 -9
  21. package/lib/rules/eqeqeq.js +31 -8
  22. package/lib/rules/index.js +2 -1
  23. package/lib/rules/max-params.js +32 -7
  24. package/lib/rules/no-array-constructor.js +51 -1
  25. package/lib/rules/no-shadow-restricted-names.js +25 -2
  26. package/lib/rules/no-unassigned-vars.js +72 -0
  27. package/lib/rules/no-unused-expressions.js +7 -1
  28. package/lib/rules/no-useless-escape.js +24 -2
  29. package/lib/rules/prefer-named-capture-group.js +7 -1
  30. package/lib/rules/utils/lazy-loading-rule-map.js +2 -2
  31. package/lib/services/processor-service.js +1 -2
  32. package/lib/services/suppressions-service.js +5 -3
  33. package/lib/shared/flags.js +1 -0
  34. package/lib/shared/serialization.js +29 -6
  35. package/lib/types/index.d.ts +126 -6
  36. package/lib/types/rules.d.ts +33 -2
  37. package/package.json +7 -6
  38. package/lib/config/flat-config-helpers.js +0 -128
  39. package/lib/config/rule-validator.js +0 -199
  40. package/lib/shared/types.js +0 -246
@@ -35,7 +35,6 @@ import type {
35
35
  LanguageOptions as GenericLanguageOptions,
36
36
  RuleDefinition,
37
37
  RuleContext as CoreRuleContext,
38
- RuleContextTypeOptions,
39
38
  DeprecatedInfo,
40
39
  } from "@eslint/core";
41
40
  import { JSONSchema4 } from "json-schema";
@@ -1561,9 +1560,16 @@ export namespace Linter {
1561
1560
  /**
1562
1561
  * Parser options.
1563
1562
  *
1564
- * @see [Specifying Parser Options](https://eslint.org/docs/latest/use/configure/language-options-deprecated#specifying-parser-options)
1563
+ * @see [Specifying Parser Options](https://eslint.org/docs/latest/use/configure/language-options#specifying-parser-options)
1565
1564
  */
1566
1565
  interface ParserOptions {
1566
+ /**
1567
+ * Allow the use of reserved words as identifiers (if `ecmaVersion` is 3).
1568
+ *
1569
+ * @default false
1570
+ */
1571
+ allowReserved?: boolean | undefined;
1572
+
1567
1573
  /**
1568
1574
  * Accepts any valid ECMAScript version number or `'latest'`:
1569
1575
  *
@@ -1620,26 +1626,54 @@ export namespace Linter {
1620
1626
  }
1621
1627
 
1622
1628
  interface LintSuggestion {
1629
+ /** A short description. */
1623
1630
  desc: string;
1631
+
1632
+ /** Fix result info. */
1624
1633
  fix: Rule.Fix;
1634
+
1635
+ /** Id referencing a message for the description. */
1625
1636
  messageId?: string | undefined;
1626
1637
  }
1627
1638
 
1628
1639
  interface LintMessage {
1640
+ /** The 1-based column number. */
1629
1641
  column: number;
1642
+
1643
+ /** The 1-based line number. */
1630
1644
  line: number;
1645
+
1646
+ /** The 1-based column number of the end location. */
1631
1647
  endColumn?: number | undefined;
1648
+
1649
+ /** The 1-based line number of the end location. */
1632
1650
  endLine?: number | undefined;
1651
+
1652
+ /** The ID of the rule which makes this message. */
1633
1653
  ruleId: string | null;
1654
+
1655
+ /** The reported message. */
1634
1656
  message: string;
1657
+
1658
+ /** The ID of the message in the rule's meta. */
1635
1659
  messageId?: string | undefined;
1660
+
1636
1661
  /**
1662
+ * Type of node.
1637
1663
  * @deprecated `nodeType` is deprecated and will be removed in the next major version.
1638
1664
  */
1639
1665
  nodeType?: string | undefined;
1666
+
1667
+ /** If `true` then this is a fatal error. */
1640
1668
  fatal?: true | undefined;
1669
+
1670
+ /** The severity of this message. */
1641
1671
  severity: Exclude<Severity, 0>;
1672
+
1673
+ /** Information for autofix. */
1642
1674
  fix?: Rule.Fix | undefined;
1675
+
1676
+ /** Information for suggestions. */
1643
1677
  suggestions?: LintSuggestion[] | undefined;
1644
1678
  }
1645
1679
 
@@ -1649,6 +1683,7 @@ export namespace Linter {
1649
1683
  }
1650
1684
 
1651
1685
  interface SuppressedLintMessage extends LintMessage {
1686
+ /** The suppression info. */
1652
1687
  suppressions: LintSuppression[];
1653
1688
  }
1654
1689
 
@@ -1662,8 +1697,8 @@ export namespace Linter {
1662
1697
  messages: LintMessage[];
1663
1698
  }
1664
1699
 
1665
- // Temporarily loosen type for just flat config files (see #68232)
1666
- type NonESTreeParser = Omit<ESTreeParser, "parseForESLint"> &
1700
+ // Temporarily loosen type for just flat config files (see https://github.com/DefinitelyTyped/DefinitelyTyped/pull/68232)
1701
+ type NonESTreeParser = ESLint.ObjectMetaProperties &
1667
1702
  (
1668
1703
  | {
1669
1704
  parse(text: string, options?: any): unknown;
@@ -1688,9 +1723,16 @@ export namespace Linter {
1688
1723
  type Parser = NonESTreeParser | ESTreeParser;
1689
1724
 
1690
1725
  interface ESLintParseResult {
1726
+ /** The AST object. */
1691
1727
  ast: AST.Program;
1692
- parserServices?: SourceCode.ParserServices | undefined;
1728
+
1729
+ /** The services that the parser provides. */
1730
+ services?: SourceCode.ParserServices | undefined;
1731
+
1732
+ /** The scope manager of the AST. */
1693
1733
  scopeManager?: Scope.ScopeManager | undefined;
1734
+
1735
+ /** The visitor keys of the AST. */
1694
1736
  visitorKeys?: SourceCode.VisitorKeys | undefined;
1695
1737
  }
1696
1738
 
@@ -1703,8 +1745,13 @@ export namespace Linter {
1703
1745
  interface Processor<
1704
1746
  T extends string | ProcessorFile = string | ProcessorFile,
1705
1747
  > extends ESLint.ObjectMetaProperties {
1748
+ /** If `true` then it means the processor supports autofix. */
1706
1749
  supportsAutofix?: boolean | undefined;
1750
+
1751
+ /** The function to extract code blocks. */
1707
1752
  preprocess?(text: string, filename: string): T[];
1753
+
1754
+ /** The function to merge messages. */
1708
1755
  postprocess?(
1709
1756
  messages: LintMessage[][],
1710
1757
  filename: string,
@@ -1845,6 +1892,9 @@ export namespace Linter {
1845
1892
  reportUnusedInlineConfigs?: Severity | StringSeverity;
1846
1893
  }
1847
1894
 
1895
+ /**
1896
+ * Performance statistics.
1897
+ */
1848
1898
  interface Stats {
1849
1899
  /**
1850
1900
  * The number of times ESLint has applied at least one fix after linting.
@@ -1858,9 +1908,24 @@ export namespace Linter {
1858
1908
  }
1859
1909
 
1860
1910
  interface TimePass {
1911
+ /**
1912
+ * The parse object containing all parse time information.
1913
+ */
1861
1914
  parse: { total: number };
1915
+
1916
+ /**
1917
+ * The rules object containing all lint time information for each rule.
1918
+ */
1862
1919
  rules?: Record<string, { total: number }>;
1920
+
1921
+ /**
1922
+ * The fix object containing all fix time information.
1923
+ */
1863
1924
  fix: { total: number };
1925
+
1926
+ /**
1927
+ * The total time that is spent on (parsing, fixing, linting) a file.
1928
+ */
1864
1929
  total: number;
1865
1930
  }
1866
1931
  }
@@ -1916,7 +1981,10 @@ export namespace ESLint {
1916
1981
  Omit<Linter.LegacyConfig<Rules>, "$schema">;
1917
1982
 
1918
1983
  interface Environment {
1984
+ /** The definition of global variables. */
1919
1985
  globals?: Linter.Globals | undefined;
1986
+
1987
+ /** The parser options that will be enabled under this environment. */
1920
1988
  parserOptions?: Linter.ParserOptions | undefined;
1921
1989
  }
1922
1990
 
@@ -1934,6 +2002,9 @@ export namespace ESLint {
1934
2002
  }
1935
2003
 
1936
2004
  interface Plugin extends ObjectMetaProperties {
2005
+ meta?: ObjectMetaProperties["meta"] & {
2006
+ namespace?: string | undefined;
2007
+ };
1937
2008
  configs?:
1938
2009
  | Record<
1939
2010
  string,
@@ -2020,21 +2091,48 @@ export namespace ESLint {
2020
2091
  flags?: string[] | undefined;
2021
2092
  }
2022
2093
 
2094
+ /** A linting result. */
2023
2095
  interface LintResult {
2096
+ /** The path to the file that was linted. */
2024
2097
  filePath: string;
2098
+
2099
+ /** All of the messages for the result. */
2025
2100
  messages: Linter.LintMessage[];
2101
+
2102
+ /** All of the suppressed messages for the result. */
2026
2103
  suppressedMessages: Linter.SuppressedLintMessage[];
2104
+
2105
+ /** Number of errors for the result. */
2027
2106
  errorCount: number;
2107
+
2108
+ /** Number of fatal errors for the result. */
2028
2109
  fatalErrorCount: number;
2110
+
2111
+ /** Number of warnings for the result. */
2029
2112
  warningCount: number;
2113
+
2114
+ /** Number of fixable errors for the result. */
2030
2115
  fixableErrorCount: number;
2116
+
2117
+ /** Number of fixable warnings for the result. */
2031
2118
  fixableWarningCount: number;
2119
+
2120
+ /** The source code of the file that was linted, with as many fixes applied as possible. */
2032
2121
  output?: string | undefined;
2122
+
2123
+ /** The source code of the file that was linted. */
2033
2124
  source?: string | undefined;
2125
+
2126
+ /** The performance statistics collected with the `stats` flag. */
2034
2127
  stats?: Linter.Stats | undefined;
2128
+
2129
+ /** The list of used deprecated rules. */
2035
2130
  usedDeprecatedRules: DeprecatedRuleUse[];
2036
2131
  }
2037
2132
 
2133
+ /**
2134
+ * Information provided when the maximum warning threshold is exceeded.
2135
+ */
2038
2136
  interface MaxWarningsExceeded {
2039
2137
  /**
2040
2138
  * Number of warnings to trigger nonzero exit code.
@@ -2055,12 +2153,34 @@ export namespace ESLint {
2055
2153
  };
2056
2154
  }
2057
2155
 
2156
+ /**
2157
+ * Information about deprecated rules.
2158
+ */
2058
2159
  interface DeprecatedRuleUse {
2160
+ /**
2161
+ * The rule ID.
2162
+ */
2059
2163
  ruleId: string;
2164
+
2165
+ /**
2166
+ * The rule IDs that replace this deprecated rule.
2167
+ */
2060
2168
  replacedBy: string[];
2169
+
2170
+ /**
2171
+ * The raw deprecated info provided by the rule.
2172
+ * Unset if the rule's `meta.deprecated` property is a boolean.
2173
+ */
2174
+ info?: DeprecatedInfo;
2061
2175
  }
2062
2176
 
2177
+ /**
2178
+ * Metadata about results for formatters.
2179
+ */
2063
2180
  interface ResultsMeta {
2181
+ /**
2182
+ * Present if the maxWarnings threshold was exceeded.
2183
+ */
2064
2184
  maxWarningsExceeded?: MaxWarningsExceeded | undefined;
2065
2185
  }
2066
2186
 
@@ -2121,7 +2241,7 @@ export class RuleTester {
2121
2241
 
2122
2242
  run(
2123
2243
  name: string,
2124
- rule: Rule.RuleModule,
2244
+ rule: RuleDefinition,
2125
2245
  tests: {
2126
2246
  valid: Array<string | RuleTester.ValidTestCase>;
2127
2247
  invalid: RuleTester.InvalidTestCase[];
@@ -1800,6 +1800,10 @@ export interface ESLintRules extends Linter.RulesRecord {
1800
1800
  * @default 3
1801
1801
  */
1802
1802
  max: number;
1803
+ /**
1804
+ * @default false
1805
+ */
1806
+ countVoidThis: boolean;
1803
1807
  }>
1804
1808
  | number,
1805
1809
  ]
@@ -3578,7 +3582,16 @@ export interface ESLintRules extends Linter.RulesRecord {
3578
3582
  * @since 0.1.4
3579
3583
  * @see https://eslint.org/docs/latest/rules/no-shadow-restricted-names
3580
3584
  */
3581
- "no-shadow-restricted-names": Linter.RuleEntry<[]>;
3585
+ "no-shadow-restricted-names": Linter.RuleEntry<
3586
+ [
3587
+ Partial<{
3588
+ /**
3589
+ * @default false
3590
+ */
3591
+ reportGlobalThis: boolean;
3592
+ }>,
3593
+ ]
3594
+ >;
3582
3595
 
3583
3596
  /**
3584
3597
  * Rule to disallow spacing between function identifiers and their applications (deprecated).
@@ -3701,6 +3714,14 @@ export interface ESLintRules extends Linter.RulesRecord {
3701
3714
  ]
3702
3715
  >;
3703
3716
 
3717
+ /**
3718
+ * Rule to disallow `let` or `var` variables that are read but never assigned.
3719
+ *
3720
+ * @since 9.27.0
3721
+ * @see https://eslint.org/docs/latest/rules/no-unassigned-vars
3722
+ */
3723
+ "no-unassigned-vars": Linter.RuleEntry<[]>;
3724
+
3704
3725
  /**
3705
3726
  * Rule to disallow the use of undeclared variables unless mentioned in \/*global *\/ comments.
3706
3727
  *
@@ -3935,6 +3956,10 @@ export interface ESLintRules extends Linter.RulesRecord {
3935
3956
  * @default false
3936
3957
  */
3937
3958
  enforceForJSX: boolean;
3959
+ /**
3960
+ * @default false
3961
+ */
3962
+ ignoreDirectives: boolean;
3938
3963
  }>,
3939
3964
  ]
3940
3965
  >;
@@ -4117,7 +4142,13 @@ export interface ESLintRules extends Linter.RulesRecord {
4117
4142
  * @since 2.5.0
4118
4143
  * @see https://eslint.org/docs/latest/rules/no-useless-escape
4119
4144
  */
4120
- "no-useless-escape": Linter.RuleEntry<[]>;
4145
+ "no-useless-escape": Linter.RuleEntry<
4146
+ [
4147
+ Partial<{
4148
+ allowRegexCharacters: string[];
4149
+ }>,
4150
+ ]
4151
+ >;
4121
4152
 
4122
4153
  /**
4123
4154
  * 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.25.1",
3
+ "version": "9.27.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,10 +108,10 @@
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.25.1",
114
- "@eslint/plugin-kit": "^0.2.8",
113
+ "@eslint/js": "9.27.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",
@@ -141,12 +141,13 @@
141
141
  "optionator": "^0.9.3"
142
142
  },
143
143
  "devDependencies": {
144
- "@arethetypeswrong/cli": "^0.17.0",
144
+ "@arethetypeswrong/cli": "^0.18.0",
145
145
  "@babel/core": "^7.4.3",
146
146
  "@babel/preset-env": "^7.4.3",
147
147
  "@cypress/webpack-preprocessor": "^6.0.2",
148
- "@eslint/json": "^0.11.0",
148
+ "@eslint/json": "^0.12.0",
149
149
  "@trunkio/launcher": "^1.3.4",
150
+ "@types/esquery": "^1.5.4",
150
151
  "@types/node": "^22.13.14",
151
152
  "@typescript-eslint/parser": "^8.4.0",
152
153
  "babel-loader": "^8.0.5",
@@ -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;