From e64e2608c725a7786cecd843044d4b7d74117cac Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Thu, 22 Feb 2024 08:36:30 +1030 Subject: [PATCH] [New] export flat configs from plugin root and fix flat config crash Co-authored-by: Brad Zacher Co-authored-by: Milos Djermanovic --- .eslint-doc-generatorrc.js | 2 +- .eslintrc | 1 + CHANGELOG.md | 9 +- README.md | 31 ++--- configs/all.js | 46 +------ configs/jsx-runtime.js | 21 ++-- configs/recommended.js | 37 ++---- index.js | 106 +++++++++++++--- lib/rules/index.js | 2 +- package.json | 2 +- .../config-all/eslint.config-deep.js | 11 ++ .../config-all/eslint.config-root.js | 8 ++ .../fixtures/flat-config/config-all/test.jsx | 3 + .../config-jsx-runtime/eslint.config-deep.js | 15 +++ .../config-jsx-runtime/eslint.config-root.js | 11 ++ .../flat-config/config-jsx-runtime/test.jsx | 3 + .../config-recommended/eslint.config-deep.js | 11 ++ .../config-recommended/eslint.config-root.js | 8 ++ .../flat-config/config-recommended/test.jsx | 3 + .../plugin-and-config/eslint.config-deep.js | 18 +++ .../plugin-and-config/eslint.config-root.js | 15 +++ .../flat-config/plugin-and-config/test.jsx | 3 + .../flat-config/plugin/eslint.config.js | 20 +++ tests/fixtures/flat-config/plugin/test.jsx | 3 + tests/flat-config.js | 117 ++++++++++++++++++ 25 files changed, 386 insertions(+), 120 deletions(-) create mode 100644 tests/fixtures/flat-config/config-all/eslint.config-deep.js create mode 100644 tests/fixtures/flat-config/config-all/eslint.config-root.js create mode 100644 tests/fixtures/flat-config/config-all/test.jsx create mode 100644 tests/fixtures/flat-config/config-jsx-runtime/eslint.config-deep.js create mode 100644 tests/fixtures/flat-config/config-jsx-runtime/eslint.config-root.js create mode 100644 tests/fixtures/flat-config/config-jsx-runtime/test.jsx create mode 100644 tests/fixtures/flat-config/config-recommended/eslint.config-deep.js create mode 100644 tests/fixtures/flat-config/config-recommended/eslint.config-root.js create mode 100644 tests/fixtures/flat-config/config-recommended/test.jsx create mode 100644 tests/fixtures/flat-config/plugin-and-config/eslint.config-deep.js create mode 100644 tests/fixtures/flat-config/plugin-and-config/eslint.config-root.js create mode 100644 tests/fixtures/flat-config/plugin-and-config/test.jsx create mode 100644 tests/fixtures/flat-config/plugin/eslint.config.js create mode 100644 tests/fixtures/flat-config/plugin/test.jsx create mode 100644 tests/flat-config.js diff --git a/.eslint-doc-generatorrc.js b/.eslint-doc-generatorrc.js index 1e6d1717f3..81c443bebb 100644 --- a/.eslint-doc-generatorrc.js +++ b/.eslint-doc-generatorrc.js @@ -4,7 +4,7 @@ const config = { ['jsx-runtime', '🏃'], ['recommended', '☑️'], ], - ignoreConfig: ['all'], + ignoreConfig: ['all', 'flat'], urlConfigs: 'https://github.com/jsx-eslint/eslint-plugin-react/#shareable-configs', }; diff --git a/.eslintrc b/.eslintrc index e609aa23be..b77f8a94e7 100644 --- a/.eslintrc +++ b/.eslintrc @@ -16,6 +16,7 @@ "ignorePatterns": [ "coverage/", ".nyc_output/", + "tests/fixtures/flat-config/" ], "rules": { "comma-dangle": [2, "always-multiline"], diff --git a/CHANGELOG.md b/CHANGELOG.md index d64f8ed17e..2c62eed275 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,17 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## Unreleased +### Added +* export flat configs from plugin root and fix flat config crash ([#3694][] @bradzacher @mdjermanovic) + +[#3694]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3694 + ## [7.34.4] - 2024.07.13 ### Fixed -* [`prop-types`]: fix `className` missing in prop validation false negative ([#3749] @akulsr0) -* [`sort-prop-types`]: Check for undefined before accessing `node.typeAnnotation.typeAnnotation` ([#3779] @tylerlaprade) +* [`prop-types`]: fix `className` missing in prop validation false negative ([#3749][] @akulsr0) +* [`sort-prop-types`]: Check for undefined before accessing `node.typeAnnotation.typeAnnotation` ([#3779][] @tylerlaprade) [7.34.4]: https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.34.3...v7.34.4 [#3779]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3779 diff --git a/README.md b/README.md index 64e57912a1..5528152083 100644 --- a/README.md +++ b/README.md @@ -203,27 +203,22 @@ Refer to the [official docs](https://eslint.org/docs/latest/user-guide/configuri The schema of the `settings.react` object would be identical to that of what's already described above in the legacy config section. -### Shareable configs - -There're also 3 shareable configs. - -- `eslint-plugin-react/configs/all` -- `eslint-plugin-react/configs/recommended` -- `eslint-plugin-react/configs/jsx-runtime` +### Flat Configs -If your eslint.config.js is ESM, include the `.js` extension (e.g. `eslint-plugin-react/recommended.js`). Note that the next semver-major will require omitting the extension for these imports. +This plugin exports 3 flat configs: -**Note**: These configurations will import `eslint-plugin-react` and enable JSX in [`languageOptions.parserOptions`](https://eslint.org/docs/latest/user-guide/configuring/configuration-files-new#configuration-objects). +- `flat.all` +- `flat.recommended` +- `flat['jsx-runtime']` -In the new config system, `plugin:` protocol(e.g. `plugin:react/recommended`) is no longer valid. -As eslint does not automatically import the preset config (shareable config), you explicitly do it by yourself. +The flat configs are available via the root plugin import. They will configure the plugin under the `react/` namespace and enable JSX in [`languageOptions.parserOptions`](https://eslint.org/docs/latest/use/configure/language-options#specifying-parser-options). ```js -const reactRecommended = require('eslint-plugin-react/configs/recommended'); +const reactPlugin = require('eslint-plugin-react'); module.exports = [ … - reactRecommended, // This is not a plugin object, but a shareable config object + reactPlugin.configs.flat.recommended, // This is not a plugin object, but a shareable config object … ]; ``` @@ -234,16 +229,16 @@ You can of course add/override some properties. For most of the cases, you probably want to configure some properties by yourself. ```js -const reactRecommended = require('eslint-plugin-react/configs/recommended'); +const reactPlugin = require('eslint-plugin-react'); const globals = require('globals'); module.exports = [ … { files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'], - ...reactRecommended, + ...reactPlugin.configs.flat.recommended, languageOptions: { - ...reactRecommended.languageOptions, + ...reactPlugin.configs.flat.recommended.languageOptions, globals: { ...globals.serviceworker, ...globals.browser, @@ -257,14 +252,14 @@ module.exports = [ The above example is same as the example below, as the new config system is based on chaining. ```js -const reactRecommended = require('eslint-plugin-react/configs/recommended'); +const reactPlugin = require('eslint-plugin-react'); const globals = require('globals'); module.exports = [ … { files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'], - ...reactRecommended, + ...reactPlugin.configs.flat.recommended, }, { files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'], diff --git a/configs/all.js b/configs/all.js index bcb416e295..3123c5bd5d 100644 --- a/configs/all.js +++ b/configs/all.js @@ -1,49 +1,13 @@ 'use strict'; -const fromEntries = require('object.fromentries'); -const entries = require('object.entries'); +const plugin = require('..'); -const allRules = require('../lib/rules'); - -function filterRules(rules, predicate) { - return fromEntries(entries(rules).filter((entry) => predicate(entry[1]))); -} - -/** - * @param {object} rules - rules object mapping rule name to rule module - * @returns {Record} - */ -function configureAsError(rules) { - return fromEntries(Object.keys(rules).map((key) => [`react/${key}`, 2])); -} - -const activeRules = filterRules(allRules, (rule) => !rule.meta.deprecated); -const activeRulesConfig = configureAsError(activeRules); - -const deprecatedRules = filterRules(allRules, (rule) => rule.meta.deprecated); +const legacyConfig = plugin.configs.all; module.exports = { - plugins: { - /** - * @type {{ - * deprecatedRules: Record, - * rules: Record, - * }} - */ - react: { - deprecatedRules, - rules: allRules, - }, - }, - rules: activeRulesConfig, - languageOptions: { - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - }, + plugins: { react: plugin }, + rules: legacyConfig.rules, + languageOptions: { parserOptions: legacyConfig.parserOptions }, }; -// this is so the `languageOptions` property won't be warned in the new config system Object.defineProperty(module.exports, 'languageOptions', { enumerable: false }); diff --git a/configs/jsx-runtime.js b/configs/jsx-runtime.js index a6302be504..f5cd178587 100644 --- a/configs/jsx-runtime.js +++ b/configs/jsx-runtime.js @@ -1,18 +1,13 @@ 'use strict'; -const all = require('./all'); +const plugin = require('..'); -module.exports = Object.assign({}, all, { - languageOptions: Object.assign({}, all.languageOptions, { - parserOptions: Object.assign({}, all.languageOptions.parserOptions, { - jsxPragma: null, // for @typescript/eslint-parser - }), - }), - rules: { - 'react/react-in-jsx-scope': 0, - 'react/jsx-uses-react': 0, - }, -}); +const legacyConfig = plugin.configs['jsx-runtime']; + +module.exports = { + plugins: { react: plugin }, + rules: legacyConfig.rules, + languageOptions: { parserOptions: legacyConfig.parserOptions }, +}; -// this is so the `languageOptions` property won't be warned in the new config system Object.defineProperty(module.exports, 'languageOptions', { enumerable: false }); diff --git a/configs/recommended.js b/configs/recommended.js index 0bba09f7bb..19cbda4cf4 100644 --- a/configs/recommended.js +++ b/configs/recommended.js @@ -1,34 +1,13 @@ 'use strict'; -const all = require('./all'); +const plugin = require('..'); -module.exports = Object.assign({}, all, { - languageOptions: all.languageOptions, - rules: { - 'react/display-name': 2, - 'react/jsx-key': 2, - 'react/jsx-no-comment-textnodes': 2, - 'react/jsx-no-duplicate-props': 2, - 'react/jsx-no-target-blank': 2, - 'react/jsx-no-undef': 2, - 'react/jsx-uses-react': 2, - 'react/jsx-uses-vars': 2, - 'react/no-children-prop': 2, - 'react/no-danger-with-children': 2, - 'react/no-deprecated': 2, - 'react/no-direct-mutation-state': 2, - 'react/no-find-dom-node': 2, - 'react/no-is-mounted': 2, - 'react/no-render-return-value': 2, - 'react/no-string-refs': 2, - 'react/no-unescaped-entities': 2, - 'react/no-unknown-property': 2, - 'react/no-unsafe': 0, - 'react/prop-types': 2, - 'react/react-in-jsx-scope': 2, - 'react/require-render-return': 2, - }, -}); +const legacyConfig = plugin.configs.recommended; + +module.exports = { + plugins: { react: plugin }, + rules: legacyConfig.rules, + languageOptions: { parserOptions: legacyConfig.parserOptions }, +}; -// this is so the `languageOptions` property won't be warned in the new config system Object.defineProperty(module.exports, 'languageOptions', { enumerable: false }); diff --git a/index.js b/index.js index 4140c6c881..d1b2c120aa 100644 --- a/index.js +++ b/index.js @@ -1,31 +1,109 @@ 'use strict'; -const configAll = require('./configs/all'); -const configRecommended = require('./configs/recommended'); -const configRuntime = require('./configs/jsx-runtime'); +const fromEntries = require('object.fromentries'); +const entries = require('object.entries'); const allRules = require('./lib/rules'); +function filterRules(rules, predicate) { + return fromEntries(entries(rules).filter((entry) => predicate(entry[1]))); +} + +/** + * @param {object} rules - rules object mapping rule name to rule module + * @returns {Record} + */ +function configureAsError(rules) { + return fromEntries(Object.keys(rules).map((key) => [`react/${key}`, 2])); +} + +const activeRules = filterRules(allRules, (rule) => !rule.meta.deprecated); +const activeRulesConfig = configureAsError(activeRules); + +const deprecatedRules = filterRules(allRules, (rule) => rule.meta.deprecated); + // for legacy config system const plugins = [ 'react', ]; -module.exports = { - deprecatedRules: configAll.plugins.react.deprecatedRules, +const plugin = { + deprecatedRules, rules: allRules, configs: { - recommended: Object.assign({}, configRecommended, { - parserOptions: configRecommended.languageOptions.parserOptions, + recommended: { plugins, - }), - all: Object.assign({}, configAll, { - parserOptions: configAll.languageOptions.parserOptions, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + rules: { + 'react/display-name': 2, + 'react/jsx-key': 2, + 'react/jsx-no-comment-textnodes': 2, + 'react/jsx-no-duplicate-props': 2, + 'react/jsx-no-target-blank': 2, + 'react/jsx-no-undef': 2, + 'react/jsx-uses-react': 2, + 'react/jsx-uses-vars': 2, + 'react/no-children-prop': 2, + 'react/no-danger-with-children': 2, + 'react/no-deprecated': 2, + 'react/no-direct-mutation-state': 2, + 'react/no-find-dom-node': 2, + 'react/no-is-mounted': 2, + 'react/no-render-return-value': 2, + 'react/no-string-refs': 2, + 'react/no-unescaped-entities': 2, + 'react/no-unknown-property': 2, + 'react/no-unsafe': 0, + 'react/prop-types': 2, + 'react/react-in-jsx-scope': 2, + 'react/require-render-return': 2, + }, + }, + all: { plugins, - }), - 'jsx-runtime': Object.assign({}, configRuntime, { - parserOptions: configRuntime.languageOptions.parserOptions, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + rules: activeRulesConfig, + }, + 'jsx-runtime': { plugins, - }), + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + jsxPragma: null, // for @typescript/eslint-parser + }, + rules: { + 'react/react-in-jsx-scope': 0, + 'react/jsx-uses-react': 0, + }, + }, + }, +}; + +plugin.configs.flat = { + recommended: { + plugins: { react: plugin }, + rules: plugin.configs.recommended.rules, + languageOptions: { parserOptions: plugin.configs.recommended.parserOptions }, + }, + all: { + plugins: { react: plugin }, + rules: plugin.configs.all.rules, + languageOptions: { parserOptions: plugin.configs.all.parserOptions }, + }, + 'jsx-runtime': { + plugins: { react: plugin }, + rules: plugin.configs['jsx-runtime'].rules, + languageOptions: { parserOptions: plugin.configs['jsx-runtime'].parserOptions }, }, }; + +module.exports = plugin; diff --git a/lib/rules/index.js b/lib/rules/index.js index 784831bba7..c30dc6e609 100644 --- a/lib/rules/index.js +++ b/lib/rules/index.js @@ -2,7 +2,7 @@ /* eslint global-require: 0 */ -/** @type {Record} */ +/** @satisfies {Record} */ module.exports = { 'boolean-prop-naming': require('./boolean-prop-naming'), 'button-has-type': require('./button-has-type'), diff --git a/package.json b/package.json index bf26317911..72de675758 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "test": "npm run unit-test", "posttest": "aud --production", "type-check": "tsc", - "unit-test": "istanbul cover node_modules/mocha/bin/_mocha tests/lib/**/*.js tests/util/**/*.js tests/index.js", + "unit-test": "istanbul cover node_modules/mocha/bin/_mocha tests/lib/**/*.js tests/util/**/*.js tests/index.js tests/flat-config.js", "update:eslint-docs": "eslint-doc-generator" }, "repository": { diff --git a/tests/fixtures/flat-config/config-all/eslint.config-deep.js b/tests/fixtures/flat-config/config-all/eslint.config-deep.js new file mode 100644 index 0000000000..e74b950f50 --- /dev/null +++ b/tests/fixtures/flat-config/config-all/eslint.config-deep.js @@ -0,0 +1,11 @@ +'use strict'; + +const reactAll = require('../../../../configs/all'); + +module.exports = [{ + files: ['**/*.jsx'], + ...reactAll, + languageOptions: { + ...reactAll.languageOptions + } +}]; diff --git a/tests/fixtures/flat-config/config-all/eslint.config-root.js b/tests/fixtures/flat-config/config-all/eslint.config-root.js new file mode 100644 index 0000000000..f5dc21e002 --- /dev/null +++ b/tests/fixtures/flat-config/config-all/eslint.config-root.js @@ -0,0 +1,8 @@ +'use strict'; + +const reactPlugin = require('../../../..'); + +module.exports = [{ + files: ['**/*.jsx'], + ...reactPlugin.configs.flat.all +}]; diff --git a/tests/fixtures/flat-config/config-all/test.jsx b/tests/fixtures/flat-config/config-all/test.jsx new file mode 100644 index 0000000000..66e96ef9ed --- /dev/null +++ b/tests/fixtures/flat-config/config-all/test.jsx @@ -0,0 +1,3 @@ +
+ test +
diff --git a/tests/fixtures/flat-config/config-jsx-runtime/eslint.config-deep.js b/tests/fixtures/flat-config/config-jsx-runtime/eslint.config-deep.js new file mode 100644 index 0000000000..053bf08dab --- /dev/null +++ b/tests/fixtures/flat-config/config-jsx-runtime/eslint.config-deep.js @@ -0,0 +1,15 @@ +'use strict'; + +const reactRecommended = require('../../../../configs/recommended'); +const reactJSXRuntime = require('../../../../configs/jsx-runtime'); + +module.exports = [ + { + files: ['**/*.jsx'], + ...reactRecommended, + languageOptions: { + ...reactRecommended.languageOptions + } + }, + reactJSXRuntime +]; diff --git a/tests/fixtures/flat-config/config-jsx-runtime/eslint.config-root.js b/tests/fixtures/flat-config/config-jsx-runtime/eslint.config-root.js new file mode 100644 index 0000000000..f6c1c4aff5 --- /dev/null +++ b/tests/fixtures/flat-config/config-jsx-runtime/eslint.config-root.js @@ -0,0 +1,11 @@ +'use strict'; + +const reactPlugin = require('../../../..'); + +module.exports = [ + { + files: ['**/*.jsx'], + ...reactPlugin.configs.flat.recommended + }, + reactPlugin.configs.flat['jsx-runtime'] +]; diff --git a/tests/fixtures/flat-config/config-jsx-runtime/test.jsx b/tests/fixtures/flat-config/config-jsx-runtime/test.jsx new file mode 100644 index 0000000000..66e96ef9ed --- /dev/null +++ b/tests/fixtures/flat-config/config-jsx-runtime/test.jsx @@ -0,0 +1,3 @@ +
+ test +
diff --git a/tests/fixtures/flat-config/config-recommended/eslint.config-deep.js b/tests/fixtures/flat-config/config-recommended/eslint.config-deep.js new file mode 100644 index 0000000000..f9471c23e1 --- /dev/null +++ b/tests/fixtures/flat-config/config-recommended/eslint.config-deep.js @@ -0,0 +1,11 @@ +'use strict'; + +const reactRecommended = require('../../../../configs/recommended'); + +module.exports = [{ + files: ['**/*.jsx'], + ...reactRecommended, + languageOptions: { + ...reactRecommended.languageOptions + } +}]; diff --git a/tests/fixtures/flat-config/config-recommended/eslint.config-root.js b/tests/fixtures/flat-config/config-recommended/eslint.config-root.js new file mode 100644 index 0000000000..c1acda025b --- /dev/null +++ b/tests/fixtures/flat-config/config-recommended/eslint.config-root.js @@ -0,0 +1,8 @@ +'use strict'; + +const reactPlugin = require('../../../..'); + +module.exports = [{ + files: ['**/*.jsx'], + ...reactPlugin.configs.flat.recommended +}]; diff --git a/tests/fixtures/flat-config/config-recommended/test.jsx b/tests/fixtures/flat-config/config-recommended/test.jsx new file mode 100644 index 0000000000..66e96ef9ed --- /dev/null +++ b/tests/fixtures/flat-config/config-recommended/test.jsx @@ -0,0 +1,3 @@ +
+ test +
diff --git a/tests/fixtures/flat-config/plugin-and-config/eslint.config-deep.js b/tests/fixtures/flat-config/plugin-and-config/eslint.config-deep.js new file mode 100644 index 0000000000..ea4882daa1 --- /dev/null +++ b/tests/fixtures/flat-config/plugin-and-config/eslint.config-deep.js @@ -0,0 +1,18 @@ +'use strict'; + +const react = require('../../../..'); +const reactRecommended = require('../../../../configs/recommended'); + +module.exports = [ + { + files: ['**/*.jsx'], + plugins: { react } + }, + { + files: ['**/*.jsx'], + ...reactRecommended, + languageOptions: { + ...reactRecommended.languageOptions + } + } +]; diff --git a/tests/fixtures/flat-config/plugin-and-config/eslint.config-root.js b/tests/fixtures/flat-config/plugin-and-config/eslint.config-root.js new file mode 100644 index 0000000000..ad174287cf --- /dev/null +++ b/tests/fixtures/flat-config/plugin-and-config/eslint.config-root.js @@ -0,0 +1,15 @@ +'use strict'; + +const react = require('../../../..'); +const reactRecommended = require('../../../../configs/recommended'); + +module.exports = [ + { + files: ['**/*.jsx'], + plugins: { react } + }, + { + files: ['**/*.jsx'], + ...react.configs.flat.recommended + } +]; diff --git a/tests/fixtures/flat-config/plugin-and-config/test.jsx b/tests/fixtures/flat-config/plugin-and-config/test.jsx new file mode 100644 index 0000000000..66e96ef9ed --- /dev/null +++ b/tests/fixtures/flat-config/plugin-and-config/test.jsx @@ -0,0 +1,3 @@ +
+ test +
diff --git a/tests/fixtures/flat-config/plugin/eslint.config.js b/tests/fixtures/flat-config/plugin/eslint.config.js new file mode 100644 index 0000000000..9945a4494e --- /dev/null +++ b/tests/fixtures/flat-config/plugin/eslint.config.js @@ -0,0 +1,20 @@ +'use strict'; + +const react = require('../../../..'); + +module.exports = [{ + files: ['**/*.jsx'], + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + plugins: { + react, + }, + rules: { + 'react/jsx-no-literals': 1, + }, +}]; diff --git a/tests/fixtures/flat-config/plugin/test.jsx b/tests/fixtures/flat-config/plugin/test.jsx new file mode 100644 index 0000000000..66e96ef9ed --- /dev/null +++ b/tests/fixtures/flat-config/plugin/test.jsx @@ -0,0 +1,3 @@ +
+ test +
diff --git a/tests/flat-config.js b/tests/flat-config.js new file mode 100644 index 0000000000..79e809657d --- /dev/null +++ b/tests/flat-config.js @@ -0,0 +1,117 @@ +/* eslint-env mocha */ + +'use strict'; + +const semver = require('semver'); +const eslintPkg = require('eslint/package.json'); + +if (!semver.satisfies(eslintPkg.version, '>= 8.23.0')) { + return; +} + +const ESLint = semver.major(eslintPkg.version) < 9 + ? require('eslint/use-at-your-own-risk').FlatESLint // eslint-disable-line import/no-unresolved -- false positive + : require('eslint').ESLint; + +const path = require('path'); +const assert = require('assert'); + +describe('eslint-plugin-react in flat config', () => { + const fixturesdDir = path.resolve(__dirname, 'fixtures', 'flat-config'); + + it('should work when the plugin is used directly', () => { + const eslint = new ESLint({ + cwd: path.resolve(fixturesdDir, 'plugin'), + }); + + return eslint.lintFiles(['test.jsx']).then((results) => { + const result = results[0]; + + assert.strictEqual(result.messages.length, 1); + assert.strictEqual(result.messages[0].severity, 1); + assert.strictEqual(result.messages[0].ruleId, 'react/jsx-no-literals'); + assert.strictEqual(result.messages[0].messageId, 'literalNotInJSXExpression'); + }); + }); + + ['root', 'deep'].forEach((configAccess) => { + const overrideConfigFile = `eslint.config-${configAccess}.js`; + + it(`should work when the plugin is used with "all" config (${configAccess})`, () => { + const eslint = new ESLint({ + cwd: path.resolve(fixturesdDir, 'config-all'), + overrideConfigFile, + }); + + return eslint.lintFiles(['test.jsx']).then((results) => { + const result = results[0]; + + assert.strictEqual(result.messages.length, 3); + assert.strictEqual(result.messages[0].severity, 2); + assert.strictEqual(result.messages[0].ruleId, 'react/react-in-jsx-scope'); + assert.strictEqual(result.messages[0].messageId, 'notInScope'); + assert.strictEqual(result.messages[1].severity, 2); + assert.strictEqual(result.messages[1].ruleId, 'react/no-unknown-property'); + assert.strictEqual(result.messages[1].messageId, 'unknownProp'); + assert.strictEqual(result.messages[2].severity, 2); + assert.strictEqual(result.messages[2].ruleId, 'react/jsx-no-literals'); + assert.strictEqual(result.messages[2].messageId, 'literalNotInJSXExpression'); + }); + }); + + it(`should work when the plugin is used with "recommended" config (${configAccess})`, () => { + const eslint = new ESLint({ + cwd: path.resolve(fixturesdDir, 'config-recommended'), + overrideConfigFile, + }); + + return eslint.lintFiles(['test.jsx']).then((results) => { + const result = results[0]; + + assert.strictEqual(result.messages.length, 2); + assert.strictEqual(result.messages[0].severity, 2); + assert.strictEqual(result.messages[0].ruleId, 'react/react-in-jsx-scope'); + assert.strictEqual(result.messages[0].messageId, 'notInScope'); + assert.strictEqual(result.messages[1].severity, 2); + assert.strictEqual(result.messages[1].ruleId, 'react/no-unknown-property'); + assert.strictEqual(result.messages[1].messageId, 'unknownProp'); + }); + }); + + it(`should work when the plugin is used with "recommended" and "jsx-runtime" configs (${configAccess})`, () => { + const eslint = new ESLint({ + cwd: path.resolve(fixturesdDir, 'config-jsx-runtime'), + overrideConfigFile, + }); + + return eslint.lintFiles(['test.jsx']).then((results) => { + const result = results[0]; + + assert.strictEqual(result.messages.length, 1); + assert.strictEqual(result.messages[0].severity, 2); + assert.strictEqual(result.messages[0].ruleId, 'react/no-unknown-property'); + assert.strictEqual(result.messages[0].messageId, 'unknownProp'); + }); + }); + + // https://github.com/jsx-eslint/eslint-plugin-react/issues/3693 + it(`should work when the plugin is used directly and with "recommended" config (${configAccess})`, () => { + const eslint = new ESLint({ + cwd: path.resolve(fixturesdDir, 'plugin-and-config'), + overrideConfigFile, + }); + + return eslint.lintFiles(['test.jsx']).then((results) => { + const result = results[0]; + + assert.strictEqual(result.messages.length, 2); + assert.strictEqual(result.messages[0].severity, 2); + assert.strictEqual(result.messages[0].ruleId, 'react/react-in-jsx-scope'); + assert.strictEqual(result.messages[0].messageId, 'notInScope'); + assert.strictEqual(result.messages[1].severity, 2); + assert.strictEqual(result.messages[1].ruleId, 'react/no-unknown-property'); + assert.strictEqual(result.messages[1].messageId, 'unknownProp'); + }); + }); + }); +});