diff --git a/lib/cli-engine/config-array-factory.js b/lib/cli-engine/config-array-factory.js index e81494ed86a..2c7a79b491e 100644 --- a/lib/cli-engine/config-array-factory.js +++ b/lib/cli-engine/config-array-factory.js @@ -286,14 +286,15 @@ function loadESLintIgnoreFile(filePath) { * Creates an error to notify about a missing config to extend from. * @param {string} configName The name of the missing config. * @param {string} importerName The name of the config that imported the missing config + * @param {string} messageTemplate The text template to source error strings from. * @returns {Error} The error object to throw * @private */ -function configMissingError(configName, importerName) { +function configInvalidError(configName, importerName, messageTemplate) { return Object.assign( new Error(`Failed to load config "${configName}" to extend from.`), { - messageTemplate: "extend-config-missing", + messageTemplate, messageData: { configName, importerName } } ); @@ -802,7 +803,7 @@ class ConfigArrayFactory { }); } - throw configMissingError(extendName, ctx.name); + throw configInvalidError(extendName, ctx.name, "extend-config-missing"); } /** @@ -814,6 +815,11 @@ class ConfigArrayFactory { */ _loadExtendedPluginConfig(extendName, ctx) { const slashIndex = extendName.lastIndexOf("/"); + + if (slashIndex === -1) { + throw configInvalidError(extendName, ctx.filePath, "plugin-invalid"); + } + const pluginName = extendName.slice("plugin:".length, slashIndex); const configName = extendName.slice(slashIndex + 1); @@ -834,7 +840,7 @@ class ConfigArrayFactory { }); } - throw plugin.error || configMissingError(extendName, ctx.filePath); + throw plugin.error || configInvalidError(extendName, ctx.filePath, "extend-config-missing"); } /** @@ -867,7 +873,7 @@ class ConfigArrayFactory { } catch (error) { /* istanbul ignore else */ if (error && error.code === "MODULE_NOT_FOUND") { - throw configMissingError(extendName, ctx.filePath); + throw configInvalidError(extendName, ctx.filePath, "extend-config-missing"); } throw error; } diff --git a/messages/plugin-invalid.txt b/messages/plugin-invalid.txt new file mode 100644 index 00000000000..3ee251821be --- /dev/null +++ b/messages/plugin-invalid.txt @@ -0,0 +1,8 @@ +"<%- configName %>" is invalid syntax for a config specifier. + +* If your intention is to extend from a configuration exported from the plugin, add the configuration name after a slash: e.g. "<%- configName %>/myConfig". +* If this is the name of a shareable config instead of a plugin, remove the "plugin:" prefix: i.e. "<%- configName.slice("plugin:".length) %>". + +"<%- configName %>" was referenced from the config file in "<%- importerName %>". + +If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team. diff --git a/tests/lib/cli-engine/config-array-factory.js b/tests/lib/cli-engine/config-array-factory.js index 086c1b0b92c..6df3d98372b 100644 --- a/tests/lib/cli-engine/config-array-factory.js +++ b/tests/lib/cli-engine/config-array-factory.js @@ -1515,6 +1515,23 @@ describe("ConfigArrayFactory", () => { }, /Failed to load config "plugin:invalid-config\/bar" to extend from./u); }); + it("should throw an error with a message template when a plugin config specifier is missing config name", () => { + try { + applyExtends({ + extends: "plugin:some-plugin", + rules: { eqeqeq: 2 } + }); + } catch (err) { + assert.strictEqual(err.messageTemplate, "plugin-invalid"); + assert.deepStrictEqual(err.messageData, { + configName: "plugin:some-plugin", + importerName: path.join(process.cwd(), "whatever") + }); + return; + } + assert.fail("Expected to throw an error"); + }); + it("should throw an error with a message template when a plugin referenced for a plugin config is not found", () => { try { applyExtends({