Skip to content

Commit a686079

Browse files
Joe Stilesljharb
Joe Stiles
authored andcommitted
[New] jsx-newline: Add prevent option
Fixes #2793. Closes #2927.
1 parent bf8dff0 commit a686079

File tree

5 files changed

+368
-38
lines changed

5 files changed

+368
-38
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
99
* [`jsx-no-target-blank`]: add fixer ([#2862][] @Nokel81)
1010
* [`jsx-pascal-case`]: support minimatch `ignore` option ([#2906][] @bcherny)
1111
* [`jsx-pascal-case`]: support `allowNamespace` option ([#2917][] @kev-y-huang)
12+
* [`jsx-newline`]: Add prevent option ([#2935][] @jsphstls)
1213

1314
### Fixed
1415
* [`jsx-no-constructed-context-values`]: avoid a crash with `as X` TS code ([#2894][] @ljharb)
@@ -28,6 +29,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
2829
* [Docs] added missing curly braces ([#2923][] @Muditxofficial)
2930

3031
[#2943]: https://github.com/yannickcr/eslint-plugin-react/pull/2943
32+
[#2935]: https://github.com/yannickcr/eslint-plugin-react/pull/2935
3133
[#2930]: https://github.com/yannickcr/eslint-plugin-react/pull/2930
3234
[#2929]: https://github.com/yannickcr/eslint-plugin-react/pull/2929
3335
[#2925]: https://github.com/yannickcr/eslint-plugin-react/pull/2925

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ Enable the rules that you would like to use.
179179
|| | [react/jsx-key](docs/rules/jsx-key.md) | Report missing `key` props in iterators/collection literals |
180180
| | | [react/jsx-max-depth](docs/rules/jsx-max-depth.md) | Validate JSX maximum depth |
181181
| | 🔧 | [react/jsx-max-props-per-line](docs/rules/jsx-max-props-per-line.md) | Limit maximum of props on a single line in JSX |
182-
| | 🔧 | [react/jsx-newline](docs/rules/jsx-newline.md) | Enforce a new line after jsx elements and expressions |
182+
| | 🔧 | [react/jsx-newline](docs/rules/jsx-newline.md) | Require or prevent a new line after jsx elements and expressions |
183183
| | | [react/jsx-no-bind](docs/rules/jsx-no-bind.md) | Prevents usage of Function.prototype.bind and arrow functions in React component props |
184184
|| | [react/jsx-no-comment-textnodes](docs/rules/jsx-no-comment-textnodes.md) | Comments inside children section of tag should be placed inside braces |
185185
| | | [react/jsx-no-constructed-context-values](docs/rules/jsx-no-constructed-context-values.md) | Prevents JSX context provider values from taking values that will cause needless rerenders. |

docs/rules/jsx-newline.md

+71-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
1-
# Enforce a new line after jsx elements and expressions (react/jsx-newline)
1+
# Require or prevent a new line after jsx elements and expressions. (react/jsx-newline)
22

33
**Fixable:** This rule is automatically fixable using the `--fix` flag on the command line.
44

55
## Rule Details
66

7-
This is a stylistic rule intended to make JSX code more readable by enforcing spaces between adjacent JSX elements and expressions.
7+
This is a stylistic rule intended to make JSX code more readable by requiring or preventing lines between adjacent JSX elements and expressions.
88

9-
Examples of **incorrect** code for this rule:
9+
## Rule Options
10+
```json
11+
...
12+
"react/jsx-new-line": [<enabled>, { "prevent": <boolean> }]
13+
...
14+
```
15+
16+
* enabled: for enabling the rule. 0=off, 1=warn, 2=error. Defaults to 0.
17+
* prevent: optional boolean. If `true` prevents empty lines between adjacent JSX elements and expressions. Defaults to `false`.
18+
19+
## Examples
20+
21+
Examples of **incorrect** code for this rule, when configured with `{ "prevent": false }`:
1022

1123
```jsx
1224
<div>
@@ -33,7 +45,7 @@ Examples of **incorrect** code for this rule:
3345
</div>
3446
```
3547

36-
Examples of **correct** code for this rule:
48+
Examples of **correct** code for this rule, when configured with `{ "prevent": false }`:
3749

3850
```jsx
3951
<div>
@@ -60,6 +72,61 @@ Examples of **correct** code for this rule:
6072
</div>
6173
```
6274

75+
Examples of **incorrect** code for this rule, when configured with `{ "prevent": true }`:
76+
77+
78+
```jsx
79+
<div>
80+
<Button>{data.label}</Button>
81+
82+
<List />
83+
84+
<Button>
85+
<IconPreview />
86+
Button 2
87+
88+
<span></span>
89+
</Button>
90+
91+
{showSomething === true && <Something />}
92+
93+
<Button>Button 3</Button>
94+
95+
{showSomethingElse === true ? (
96+
<SomethingElse />
97+
) : (
98+
<ErrorMessage />
99+
)}
100+
</div>
101+
```
102+
103+
Examples of **correct** code for this rule, when configured with `{ "prevent": true }`:
104+
105+
```jsx
106+
<div>
107+
<Button>{data.label}</Button>
108+
<List />
109+
</div>
110+
```
111+
112+
```jsx
113+
<div>
114+
<Button>{data.label}</Button>
115+
{showSomething === true && <Something />}
116+
</div>
117+
```
118+
119+
```jsx
120+
<div>
121+
{showSomething === true && <Something />}
122+
{showSomethingElse === true ? (
123+
<SomethingElse />
124+
) : (
125+
<ErrorMessage />
126+
)}
127+
</div>
128+
```
129+
63130
## When Not To Use It
64131

65132
You can turn this rule off if you are not concerned with spacing between your JSX elements and expressions.

lib/rules/jsx-newline.js

+56-24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
2-
* @fileoverview Enforce a new line after jsx elements and expressions.
2+
* @fileoverview Require or prevent a new line after jsx elements and expressions.
33
* @author Johnny Zabala
4+
* @author Joseph Stiles
45
*/
56

67
'use strict';
@@ -14,16 +15,29 @@ const docsUrl = require('../util/docsUrl');
1415
module.exports = {
1516
meta: {
1617
docs: {
17-
description: 'Enforce a new line after jsx elements and expressions',
18+
description: 'Require or prevent a new line after jsx elements and expressions.',
1819
category: 'Stylistic Issues',
1920
recommended: false,
2021
url: docsUrl('jsx-newline')
2122
},
2223
fixable: 'code',
2324

2425
messages: {
25-
newLine: 'JSX element should start in a new line'
26-
}
26+
require: 'JSX element should start in a new line',
27+
prevent: 'JSX element should not start in a new line'
28+
},
29+
schema: [
30+
{
31+
type: 'object',
32+
properties: {
33+
prevent: {
34+
default: false,
35+
type: 'boolean'
36+
}
37+
},
38+
additionalProperties: false
39+
}
40+
]
2741
},
2842
create(context) {
2943
const jsxElementParents = new Set();
@@ -35,26 +49,44 @@ module.exports = {
3549
if (element.type === 'JSXElement' || element.type === 'JSXExpressionContainer') {
3650
const firstAdjacentSibling = elements[index + 1];
3751
const secondAdjacentSibling = elements[index + 2];
38-
if (
39-
firstAdjacentSibling
40-
&& secondAdjacentSibling
41-
&& (firstAdjacentSibling.type === 'Literal' || firstAdjacentSibling.type === 'JSXText')
42-
// Check adjacent sibling has the proper amount of newlines
43-
&& !/\n\s*\n/.test(firstAdjacentSibling.value)
44-
) {
45-
context.report({
46-
node: secondAdjacentSibling,
47-
messageId: 'newLine',
48-
fix(fixer) {
49-
return fixer.replaceText(
50-
firstAdjacentSibling,
51-
// double the last newline.
52-
sourceCode.getText(firstAdjacentSibling)
53-
.replace(/(\n)(?!.*\1)/g, '\n\n')
54-
);
55-
}
56-
});
57-
}
52+
53+
const hasSibling = firstAdjacentSibling
54+
&& secondAdjacentSibling
55+
&& (firstAdjacentSibling.type === 'Literal' || firstAdjacentSibling.type === 'JSXText');
56+
57+
if (!hasSibling) return;
58+
59+
// Check adjacent sibling has the proper amount of newlines
60+
const isWithoutNewLine = !/\n\s*\n/.test(firstAdjacentSibling.value);
61+
62+
const prevent = !!(context.options[0] || {}).prevent;
63+
64+
if (isWithoutNewLine === prevent) return;
65+
66+
const messageId = prevent
67+
? 'prevent'
68+
: 'require';
69+
70+
const regex = prevent
71+
? /(\n\n)(?!.*\1)/g
72+
: /(\n)(?!.*\1)/g;
73+
74+
const replacement = prevent
75+
? '\n'
76+
: '\n\n';
77+
78+
context.report({
79+
node: secondAdjacentSibling,
80+
messageId,
81+
fix(fixer) {
82+
return fixer.replaceText(
83+
firstAdjacentSibling,
84+
// double or remove the last newline
85+
sourceCode.getText(firstAdjacentSibling)
86+
.replace(regex, replacement)
87+
);
88+
}
89+
});
5890
}
5991
});
6092
});

0 commit comments

Comments
 (0)