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.
- package/README.md +44 -39
- package/bin/eslint.js +15 -0
- package/conf/rule-type-list.json +2 -1
- package/lib/cli-engine/cli-engine.js +8 -8
- package/lib/cli.js +6 -5
- package/lib/config/config-loader.js +10 -18
- package/lib/config/config.js +328 -5
- package/lib/eslint/eslint-helpers.js +3 -1
- package/lib/eslint/eslint.js +31 -17
- package/lib/eslint/legacy-eslint.js +7 -7
- package/lib/languages/js/index.js +4 -3
- package/lib/languages/js/source-code/source-code.js +10 -6
- package/lib/linter/apply-disable-directives.js +1 -1
- package/lib/linter/esquery.js +329 -0
- package/lib/linter/file-context.js +11 -0
- package/lib/linter/linter.js +81 -89
- package/lib/linter/node-event-generator.js +94 -251
- package/lib/linter/report-translator.js +2 -1
- package/lib/options.js +11 -0
- package/lib/rule-tester/rule-tester.js +17 -9
- package/lib/rules/eqeqeq.js +31 -8
- package/lib/rules/index.js +2 -1
- package/lib/rules/max-params.js +32 -7
- package/lib/rules/no-array-constructor.js +51 -1
- package/lib/rules/no-shadow-restricted-names.js +25 -2
- package/lib/rules/no-unassigned-vars.js +72 -0
- package/lib/rules/no-unused-expressions.js +7 -1
- package/lib/rules/no-useless-escape.js +24 -2
- package/lib/rules/prefer-named-capture-group.js +7 -1
- package/lib/rules/utils/lazy-loading-rule-map.js +2 -2
- package/lib/services/processor-service.js +1 -2
- package/lib/services/suppressions-service.js +5 -3
- package/lib/shared/flags.js +1 -0
- package/lib/shared/serialization.js +29 -6
- package/lib/types/index.d.ts +126 -6
- package/lib/types/rules.d.ts +33 -2
- package/package.json +7 -6
- package/lib/config/flat-config-helpers.js +0 -128
- package/lib/config/rule-validator.js +0 -199
- package/lib/shared/types.js +0 -246
package/lib/types/index.d.ts
CHANGED
@@ -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
|
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
|
1666
|
-
type NonESTreeParser =
|
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
|
-
|
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:
|
2244
|
+
rule: RuleDefinition,
|
2125
2245
|
tests: {
|
2126
2246
|
valid: Array<string | RuleTester.ValidTestCase>;
|
2127
2247
|
invalid: RuleTester.InvalidTestCase[];
|
package/lib/types/rules.d.ts
CHANGED
@@ -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.
|
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.
|
111
|
+
"@eslint/core": "^0.14.0",
|
112
112
|
"@eslint/eslintrc": "^3.3.1",
|
113
|
-
"@eslint/js": "9.
|
114
|
-
"@eslint/plugin-kit": "^0.
|
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.
|
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.
|
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;
|