Skip to content

Commit 9f4b2b9

Browse files
burtekljharb
authored andcommitted
[New] jsx-one-expression-per-line: add non-jsx option to allow non-JSX children in one line
1 parent 1014f8c commit 9f4b2b9

File tree

4 files changed

+105
-1
lines changed

4 files changed

+105
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
1717
* [`jsx-boolean-value`]: add `assumeUndefinedIsFalse` option ([#3675][] @developer-bandi)
1818
* `linkAttribute` setting, [`jsx-no-target-blank`]: support multiple properties ([#3673][] @burtek)
1919
* [`jsx-no-script-url`]: add `includeFromSettings` option to support `linkAttributes` setting ([#3673][] @burtek)
20+
* [`jsx-one-expression-per-line`]: add `non-jsx` option to allow non-JSX children in one line ([#3677][] @burtek)
2021

2122
### Fixed
2223
* [`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
3233
* [Docs] [`jsx-key`]: fix correct example ([#3656][] @developer-bandi)
3334
* [Tests] `jsx-wrap-multilines`: passing tests ([#3545][] @burtek)
3435

36+
[#3677]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3677
3537
[#3675]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3675
3638
[#3674]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3674
3739
[#3673]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3673

docs/rules/jsx-one-expression-per-line.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,11 @@ Examples of **correct** code for this rule, when configured as `"single-child"`:
133133

134134
<App><Hello /></App>
135135
```
136+
137+
Examples of **correct** code for this rule, when configured as `"non-jsx"`:
138+
139+
```jsx
140+
<App>Hello {someVariable}</App>
141+
142+
<App>Hello {<Hello />} there!</App>
143+
```

lib/rules/jsx-one-expression-per-line.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ module.exports = {
3838
type: 'object',
3939
properties: {
4040
allow: {
41-
enum: ['none', 'literal', 'single-child'],
41+
enum: ['none', 'literal', 'single-child', 'non-jsx'],
4242
},
4343
},
4444
default: optionDefaults,
@@ -65,6 +65,13 @@ module.exports = {
6565
return;
6666
}
6767

68+
if (
69+
options.allow === 'non-jsx'
70+
&& !children.find((child) => (child.type === 'JSXFragment' || child.type === 'JSXElement'))
71+
) {
72+
return;
73+
}
74+
6875
const openingElement = node.openingElement || node.openingFragment;
6976
const closingElement = node.closingElement || node.closingFragment;
7077
const openingElementStartLine = openingElement.loc.start.line;

tests/lib/rules/jsx-one-expression-per-line.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,22 @@ ruleTester.run('jsx-one-expression-per-line', rule, {
155155
code: '<App>{"foo"}</App>',
156156
options: [{ allow: 'single-child' }],
157157
},
158+
{
159+
code: '<App>123</App>',
160+
options: [{ allow: 'non-jsx' }],
161+
},
162+
{
163+
code: '<App>foo</App>',
164+
options: [{ allow: 'non-jsx' }],
165+
},
166+
{
167+
code: '<App>{"foo"}</App>',
168+
options: [{ allow: 'non-jsx' }],
169+
},
170+
{
171+
code: '<App>{<Bar />}</App>',
172+
options: [{ allow: 'non-jsx' }],
173+
},
158174
{
159175
code: '<App>{foo && <Bar />}</App>',
160176
options: [{ allow: 'single-child' }],
@@ -184,6 +200,38 @@ ruleTester.run('jsx-one-expression-per-line', rule, {
184200
`,
185201
features: ['fragment', 'no-ts-old'], // TODO: FIXME: remove no-ts-old and fix
186202
},
203+
{
204+
code: '<App>Hello {name}</App>',
205+
options: [{ allow: 'non-jsx' }],
206+
},
207+
{
208+
code: `
209+
<App>
210+
Hello {name} there!
211+
</App>`,
212+
options: [{ allow: 'non-jsx' }],
213+
},
214+
{
215+
code: `
216+
<App>
217+
Hello {<Bar />} there!
218+
</App>`,
219+
options: [{ allow: 'non-jsx' }],
220+
},
221+
{
222+
code: `
223+
<App>
224+
Hello {(<Bar />)} there!
225+
</App>`,
226+
options: [{ allow: 'non-jsx' }],
227+
},
228+
{
229+
code: `
230+
<App>
231+
Hello {(() => <Bar />)()} there!
232+
</App>`,
233+
options: [{ allow: 'non-jsx' }],
234+
},
187235
]),
188236

189237
invalid: parsers.all([
@@ -493,6 +541,28 @@ foo
493541
],
494542
parserOptions,
495543
},
544+
{
545+
code: `
546+
<Text style={styles.foo}>
547+
<Bar /> <Baz />
548+
</Text>
549+
`,
550+
output: `
551+
<Text style={styles.foo}>
552+
<Bar />${' '/* intentional trailing space */}
553+
{' '}
554+
<Baz />
555+
</Text>
556+
`,
557+
errors: [
558+
{
559+
messageId: 'moveToNewLine',
560+
data: { descriptor: 'Baz' },
561+
},
562+
],
563+
options: [{ allow: 'non-jsx' }],
564+
parserOptions,
565+
},
496566
{
497567
code: `
498568
<Text style={styles.foo}>
@@ -1257,6 +1327,23 @@ foo
12571327
},
12581328
],
12591329
},
1330+
{
1331+
code: `
1332+
<App><Foo /></App>
1333+
`,
1334+
output: `
1335+
<App>
1336+
<Foo />
1337+
</App>
1338+
`,
1339+
options: [{ allow: 'non-jsx' }],
1340+
errors: [
1341+
{
1342+
messageId: 'moveToNewLine',
1343+
data: { descriptor: 'Foo' },
1344+
},
1345+
],
1346+
},
12601347
{
12611348
code: `
12621349
<App

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy