From cf03104b278fea59ef46e09f667110f5eaaf95e3 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Wed, 19 Jul 2023 17:35:17 -0400 Subject: [PATCH] feat: Improve config error messages (#17385) * feat: Improve config error messages Includes some keys to catch known eslintrc keys when they appear in flat config. This throws a specific error that the ESLint CLI can then output a more helpful message about. fixes #17370 * Update messages/eslintrc-incompat.js Co-authored-by: Milos Djermanovic * Update messages/eslintrc-incompat.js Co-authored-by: Milos Djermanovic * Apply feedback --------- Co-authored-by: Milos Djermanovic --- lib/config/flat-config-schema.js | 49 ++++++++++++ messages/eslintrc-incompat.js | 98 +++++++++++++++++++++++ tests/lib/config/flat-config-array.js | 28 +++++++ tests/lib/rule-tester/flat-rule-tester.js | 2 +- 4 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 messages/eslintrc-incompat.js diff --git a/lib/config/flat-config-schema.js b/lib/config/flat-config-schema.js index 10d6b50ef1f..7325742adfc 100644 --- a/lib/config/flat-config-schema.js +++ b/lib/config/flat-config-schema.js @@ -212,6 +212,22 @@ function assertIsObject(value) { } } +/** + * The error type when there's an eslintrc-style options in a flat config. + */ +class IncompatibleKeyError extends Error { + + /** + * @param {string} key The invalid key. + */ + constructor(key) { + super("This appears to be in eslintrc format rather than flat config format."); + this.messageTemplate = "eslintrc-incompat"; + this.messageData = { key }; + } +} + + //----------------------------------------------------------------------------- // Low-Level Schemas //----------------------------------------------------------------------------- @@ -438,11 +454,44 @@ const sourceTypeSchema = { } }; +/** + * Creates a schema that always throws an error. Useful for warning + * about eslintrc-style keys. + * @param {string} key The eslintrc key to create a schema for. + * @returns {ObjectPropertySchema} The schema. + */ +function createEslintrcErrorSchema(key) { + return { + merge: "replace", + validate() { + throw new IncompatibleKeyError(key); + } + }; +} + +const eslintrcKeys = [ + "env", + "extends", + "globals", + "ignorePatterns", + "noInlineConfig", + "overrides", + "parser", + "parserOptions", + "reportUnusedDisableDirectives", + "root" +]; + //----------------------------------------------------------------------------- // Full schema //----------------------------------------------------------------------------- exports.flatConfigSchema = { + + // eslintrc-style keys that should always error + ...Object.fromEntries(eslintrcKeys.map(key => [key, createEslintrcErrorSchema(key)])), + + // flat config keys settings: deepObjectAssignSchema, linterOptions: { schema: { diff --git a/messages/eslintrc-incompat.js b/messages/eslintrc-incompat.js new file mode 100644 index 00000000000..deffca57c59 --- /dev/null +++ b/messages/eslintrc-incompat.js @@ -0,0 +1,98 @@ +"use strict"; + +/* eslint consistent-return: 0 -- no default case */ + +const messages = { + + env: ` +A config object is using the "env" key, which is not supported in flat config system. + +Flat config uses "languageOptions.globals" to define global variables for your files. + +Please see the following page for information on how to convert your config object into the correct format: +https://eslint.org/docs/latest/use/configure/migration-guide#configuring-language-options +`, + + extends: ` +A config object is using the "extends" key, which is not supported in flat config system. + +Instead of "extends", you can include config objects that you'd like to extend from directly in the flat config array. + +Please see the following page for more information: +https://eslint.org/docs/latest/use/configure/migration-guide#predefined-configs +`, + + globals: ` +A config object is using the "globals" key, which is not supported in flat config system. + +Flat config uses "languageOptions.globals" to define global variables for your files. + +Please see the following page for information on how to convert your config object into the correct format: +https://eslint.org/docs/latest/use/configure/migration-guide#configuring-language-options +`, + + ignorePatterns: ` +A config object is using the "ignorePatterns" key, which is not supported in flat config system. + +Flat config uses "ignores" to specify files to ignore. + +Please see the following page for information on how to convert your config object into the correct format: +https://eslint.org/docs/latest/use/configure/migration-guide#ignoring-files +`, + + noInlineConfig: ` +A config object is using the "noInlineConfig" key, which is not supported in flat config system. + +Flat config uses "linterOptions.noInlineConfig" to specify files to ignore. + +Please see the following page for information on how to convert your config object into the correct format: +https://eslint.org/docs/latest/use/configure/migration-guide#linter-options +`, + + overrides: ` +A config object is using the "overrides" key, which is not supported in flat config system. + +Flat config is an array that acts like the eslintrc "overrides" array. + +Please see the following page for information on how to convert your config object into the correct format: +https://eslint.org/docs/latest/use/configure/migration-guide#glob-based-configs +`, + + parser: ` +A config object is using the "parser" key, which is not supported in flat config system. + +Flat config uses "languageOptions.parser" to override the default parser. + +Please see the following page for information on how to convert your config object into the correct format: +https://eslint.org/docs/latest/use/configure/migration-guide#custom-parsers +`, + + parserOptions: ` +A config object is using the "parserOptions" key, which is not supported in flat config system. + +Flat config uses "languageOptions.parserOptions" to specify parser options. + +Please see the following page for information on how to convert your config object into the correct format: +https://eslint.org/docs/latest/use/configure/migration-guide#configuring-language-options +`, + + reportUnusedDisableDirectives: ` +A config object is using the "reportUnusedDisableDirectives" key, which is not supported in flat config system. + +Flat config uses "linterOptions.reportUnusedDisableDirectives" to specify files to ignore. + +Please see the following page for information on how to convert your config object into the correct format: +https://eslint.org/docs/latest/use/configure/migration-guide#linter-options +`, + + root: ` +A config object is using the "root" key, which is not supported in flat config system. + +Flat configs always act as if they are the root config file, so this key can be safely removed. +` +}; + +module.exports = function({ key }) { + + return messages[key].trim(); +}; diff --git a/tests/lib/config/flat-config-array.js b/tests/lib/config/flat-config-array.js index 65d3a729f13..10f9c31c3d2 100644 --- a/tests/lib/config/flat-config-array.js +++ b/tests/lib/config/flat-config-array.js @@ -1938,5 +1938,33 @@ describe("FlatConfigArray", () => { }); + describe("Invalid Keys", () => { + + [ + "env", + "extends", + "globals", + "ignorePatterns", + "noInlineConfig", + "overrides", + "parser", + "parserOptions", + "reportUnusedDisableDirectives", + "root" + ].forEach(key => { + + it(`should error when a ${key} key is found`, async () => { + await assertInvalidConfig([ + { + [key]: "foo" + } + ], `Key "${key}": This appears to be in eslintrc format rather than flat config format.`); + + }); + }); + + + }); + }); }); diff --git a/tests/lib/rule-tester/flat-rule-tester.js b/tests/lib/rule-tester/flat-rule-tester.js index 6c6188aee50..f5cdba967ec 100644 --- a/tests/lib/rule-tester/flat-rule-tester.js +++ b/tests/lib/rule-tester/flat-rule-tester.js @@ -1336,7 +1336,7 @@ describe("FlatRuleTester", () => { ], invalid: [] }); - }, /Unexpected key "env" found./u); + }, /Key "env": This appears to be in eslintrc format rather than flat config format/u); }); it("should pass-through the tester config to the rule", () => {