2 * @fileoverview Rule Validator
3 * @author Nicholas C. Zakas
8 //-----------------------------------------------------------------------------
10 //-----------------------------------------------------------------------------
12 const ajv
= require("../shared/ajv")();
17 } = require("./flat-config-helpers");
18 const ruleReplacements
= require("../../conf/replacements.json");
20 //-----------------------------------------------------------------------------
22 //-----------------------------------------------------------------------------
25 * Throws a helpful error when a rule cannot be found.
26 * @param {Object} ruleId The rule identifier.
27 * @param {string} ruleId.pluginName The ID of the rule to find.
28 * @param {string} ruleId.ruleName The ID of the rule to find.
29 * @param {Object} config The config to search in.
30 * @throws {TypeError} For missing plugin or rule.
33 function throwRuleNotFoundError({ pluginName
, ruleName
}, config
) {
35 const ruleId
= pluginName
=== "@" ? ruleName
: `${pluginName}/${ruleName}`;
37 const errorMessageHeader
= `Key "rules": Key "${ruleId}"`;
38 let errorMessage
= `${errorMessageHeader}: Could not find plugin "${pluginName}".`;
40 // if the plugin exists then we need to check if the rule exists
41 if (config
.plugins
&& config
.plugins
[pluginName
]) {
42 const replacementRuleName
= ruleReplacements
.rules
[ruleName
];
44 if (pluginName
=== "@" && replacementRuleName
) {
46 errorMessage
= `${errorMessageHeader}: Rule "${ruleName}" was removed and replaced by "${replacementRuleName}".`;
50 errorMessage
= `${errorMessageHeader}: Could not find "${ruleName}" in plugin "${pluginName}".`;
52 // otherwise, let's see if we can find the rule name elsewhere
53 for (const [otherPluginName
, otherPlugin
] of Object
.entries(config
.plugins
)) {
54 if (otherPlugin
.rules
&& otherPlugin
.rules
[ruleName
]) {
55 errorMessage
+= ` Did you mean "${otherPluginName}/${ruleName}"?`;
62 // falls through to throw error
65 throw new TypeError(errorMessage
);
68 //-----------------------------------------------------------------------------
70 //-----------------------------------------------------------------------------
73 * Implements validation functionality for the rules portion of a config.
78 * Creates a new instance.
83 * A collection of compiled validators for rules that have already
87 this.validators
= new WeakMap();
91 * Validates all of the rule configurations in a config against each
93 * @param {Object} config The full config to validate. This object must
94 * contain both the rules section and the plugins section.
96 * @throws {Error} If a rule's configuration does not match its schema.
104 for (const [ruleId
, ruleOptions
] of Object
.entries(config
.rules
)) {
106 // check for edge case
107 if (ruleId
=== "__proto__") {
112 * If a rule is disabled, we don't do any validation. This allows
113 * users to safely set any value to 0 or "off" without worrying
114 * that it will cause a validation error.
116 * Note: ruleOptions is always an array at this point because
117 * this validation occurs after FlatConfigArray has merged and
120 if (ruleOptions
[0] === 0) {
124 const rule
= getRuleFromConfig(ruleId
, config
);
127 throwRuleNotFoundError(parseRuleId(ruleId
), config
);
130 // Precompile and cache validator the first time
131 if (!this.validators
.has(rule
)) {
132 const schema
= getRuleOptionsSchema(rule
);
135 this.validators
.set(rule
, ajv
.compile(schema
));
139 const validateRule
= this.validators
.get(rule
);
143 validateRule(ruleOptions
.slice(1));
145 if (validateRule
.errors
) {
146 throw new Error(`Key "rules": Key "${ruleId}": ${
147 validateRule.errors.map(
148 error => `\tValue ${JSON.stringify(error.data)} ${error.message}
.\n`
157 exports
.RuleValidator
= RuleValidator
;