From 9f4b2b96d92bf61ae61e8fc88c413331efe6f0da Mon Sep 17 00:00:00 2001 From: Bartosz Date: Thu, 11 Jan 2024 18:05:18 +0100 Subject: [PATCH] [New] `jsx-one-expression-per-line`: add `non-jsx` option to allow non-JSX children in one line --- CHANGELOG.md | 2 + docs/rules/jsx-one-expression-per-line.md | 8 ++ lib/rules/jsx-one-expression-per-line.js | 9 +- .../lib/rules/jsx-one-expression-per-line.js | 87 +++++++++++++++++++ 4 files changed, 105 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0aa9588e97..0b95e4560f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange * [`jsx-boolean-value`]: add `assumeUndefinedIsFalse` option ([#3675][] @developer-bandi) * `linkAttribute` setting, [`jsx-no-target-blank`]: support multiple properties ([#3673][] @burtek) * [`jsx-no-script-url`]: add `includeFromSettings` option to support `linkAttributes` setting ([#3673][] @burtek) +* [`jsx-one-expression-per-line`]: add `non-jsx` option to allow non-JSX children in one line ([#3677][] @burtek) ### Fixed * [`jsx-no-leaked-render`]: preserve RHS parens for multiline jsx elements while fixing ([#3623][] @akulsr0) @@ -32,6 +33,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange * [Docs] [`jsx-key`]: fix correct example ([#3656][] @developer-bandi) * [Tests] `jsx-wrap-multilines`: passing tests ([#3545][] @burtek) +[#3677]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3677 [#3675]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3675 [#3674]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3674 [#3673]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3673 diff --git a/docs/rules/jsx-one-expression-per-line.md b/docs/rules/jsx-one-expression-per-line.md index 9c5fe0eac6..d2f170278b 100644 --- a/docs/rules/jsx-one-expression-per-line.md +++ b/docs/rules/jsx-one-expression-per-line.md @@ -133,3 +133,11 @@ Examples of **correct** code for this rule, when configured as `"single-child"`: ``` + +Examples of **correct** code for this rule, when configured as `"non-jsx"`: + +```jsx +Hello {someVariable} + +Hello {} there! +``` diff --git a/lib/rules/jsx-one-expression-per-line.js b/lib/rules/jsx-one-expression-per-line.js index 6d0d1575d9..4c5a9c970f 100644 --- a/lib/rules/jsx-one-expression-per-line.js +++ b/lib/rules/jsx-one-expression-per-line.js @@ -38,7 +38,7 @@ module.exports = { type: 'object', properties: { allow: { - enum: ['none', 'literal', 'single-child'], + enum: ['none', 'literal', 'single-child', 'non-jsx'], }, }, default: optionDefaults, @@ -65,6 +65,13 @@ module.exports = { return; } + if ( + options.allow === 'non-jsx' + && !children.find((child) => (child.type === 'JSXFragment' || child.type === 'JSXElement')) + ) { + return; + } + const openingElement = node.openingElement || node.openingFragment; const closingElement = node.closingElement || node.closingFragment; const openingElementStartLine = openingElement.loc.start.line; diff --git a/tests/lib/rules/jsx-one-expression-per-line.js b/tests/lib/rules/jsx-one-expression-per-line.js index 0c684c7da5..62afc430d4 100644 --- a/tests/lib/rules/jsx-one-expression-per-line.js +++ b/tests/lib/rules/jsx-one-expression-per-line.js @@ -155,6 +155,22 @@ ruleTester.run('jsx-one-expression-per-line', rule, { code: '{"foo"}', options: [{ allow: 'single-child' }], }, + { + code: '123', + options: [{ allow: 'non-jsx' }], + }, + { + code: 'foo', + options: [{ allow: 'non-jsx' }], + }, + { + code: '{"foo"}', + options: [{ allow: 'non-jsx' }], + }, + { + code: '{}', + options: [{ allow: 'non-jsx' }], + }, { code: '{foo && }', options: [{ allow: 'single-child' }], @@ -184,6 +200,38 @@ ruleTester.run('jsx-one-expression-per-line', rule, { `, features: ['fragment', 'no-ts-old'], // TODO: FIXME: remove no-ts-old and fix }, + { + code: 'Hello {name}', + options: [{ allow: 'non-jsx' }], + }, + { + code: ` + + Hello {name} there! + `, + options: [{ allow: 'non-jsx' }], + }, + { + code: ` + + Hello {} there! + `, + options: [{ allow: 'non-jsx' }], + }, + { + code: ` + + Hello {()} there! + `, + options: [{ allow: 'non-jsx' }], + }, + { + code: ` + + Hello {(() => )()} there! + `, + options: [{ allow: 'non-jsx' }], + }, ]), invalid: parsers.all([ @@ -493,6 +541,28 @@ foo ], parserOptions, }, + { + code: ` + + + + `, + output: ` + + ${' '/* intentional trailing space */} +{' '} + + + `, + errors: [ + { + messageId: 'moveToNewLine', + data: { descriptor: 'Baz' }, + }, + ], + options: [{ allow: 'non-jsx' }], + parserOptions, + }, { code: ` @@ -1257,6 +1327,23 @@ foo }, ], }, + { + code: ` + + `, + output: ` + + + + `, + options: [{ allow: 'non-jsx' }], + errors: [ + { + messageId: 'moveToNewLine', + data: { descriptor: 'Foo' }, + }, + ], + }, { code: `