diff --git a/lib/config/rule-validator.js b/lib/config/rule-validator.js index fe1aa863bfd..527a56e1799 100644 --- a/lib/config/rule-validator.js +++ b/lib/config/rule-validator.js @@ -35,16 +35,33 @@ function findRuleDefinition(ruleId, config) { pluginName = ruleIdParts.join("/"); } - if (!config.plugins || !config.plugins[pluginName]) { - throw new TypeError(`Key "rules": Key "${ruleId}": Could not find plugin "${pluginName}".`); - } + const errorMessageHeader = `Key "rules": Key "${ruleId}"`; + let errorMessage = `${errorMessageHeader}: Could not find plugin "${pluginName}".`; - if (!config.plugins[pluginName].rules || !config.plugins[pluginName].rules[ruleName]) { - throw new TypeError(`Key "rules": Key "${ruleId}": Could not find "${ruleName}" in plugin "${pluginName}".`); - } + // if the plugin exists then we need to check if the rule exists + if (config.plugins && config.plugins[pluginName]) { + + const plugin = config.plugins[pluginName]; + + // first check for exact rule match + if (plugin.rules && plugin.rules[ruleName]) { + return config.plugins[pluginName].rules[ruleName]; + } - return config.plugins[pluginName].rules[ruleName]; + errorMessage = `${errorMessageHeader}: Could not find "${ruleName}" in plugin "${pluginName}".`; + + // otherwise, let's see if we can find the rule name elsewhere + for (const [otherPluginName, otherPlugin] of Object.entries(config.plugins)) { + if (otherPlugin.rules && otherPlugin.rules[ruleName]) { + errorMessage += ` Did you mean "${otherPluginName}/${ruleName}"?`; + break; + } + } + + // falls through to throw error + } + throw new TypeError(errorMessage); } /** diff --git a/tests/lib/config/flat-config-array.js b/tests/lib/config/flat-config-array.js index fd89f8d972c..f6b099096e1 100644 --- a/tests/lib/config/flat-config-array.js +++ b/tests/lib/config/flat-config-array.js @@ -56,6 +56,16 @@ const baseConfig = { } } } + }, + test1: { + rules: { + match: {} + } + }, + test2: { + rules: { + nomatch: {} + } } } }; @@ -1278,6 +1288,39 @@ describe("FlatConfigArray", () => { ], "Key \"rules\": Key \"foo\": Expected severity of \"off\", 0, \"warn\", 1, \"error\", or 2."); }); + it("should error when rule doesn't exist", async () => { + + await assertInvalidConfig([ + { + rules: { + foox: [1, "bar"] + } + } + ], /Key "rules": Key "foox": Could not find "foox" in plugin "@"./u); + }); + + it("should error and suggest alternative when rule doesn't exist", async () => { + + await assertInvalidConfig([ + { + rules: { + "test2/match": "error" + } + } + ], /Key "rules": Key "test2\/match": Could not find "match" in plugin "test2"\. Did you mean "test1\/match"\?/u); + }); + + it("should error when plugin for rule doesn't exist", async () => { + + await assertInvalidConfig([ + { + rules: { + "doesnt-exist/match": "error" + } + } + ], /Key "rules": Key "doesnt-exist\/match": Could not find plugin "doesnt-exist"\./u); + }); + it("should error when rule options don't match schema", async () => { await assertInvalidConfig([