Skip to content

Commit 2186b4f

Browse files
committed
[New] : Enforce a new line after jsx elements and expressions
1 parent 6fc4bc0 commit 2186b4f

File tree

4 files changed

+335
-1
lines changed

4 files changed

+335
-1
lines changed

docs/rules/jsx-newline.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Enforce a new line after jsx elements and expressions (react/jsx-newline)
2+
3+
**Fixable:** This rule is automatically fixable using the `--fix` flag on the command line.
4+
5+
## Rule Details
6+
7+
This is a stylistic rule intended to make JSX code more readable by enforcing spaces between adjacent JSX elements and expressions.
8+
9+
The following patterns are considered warnings:
10+
11+
```jsx
12+
<div>
13+
<Button>{data.label}</Button>
14+
<List />
15+
</div>
16+
```
17+
18+
```jsx
19+
<div>
20+
<Button>{data.label}</Button>
21+
{showSomething === true && <Something />}
22+
</div>
23+
```
24+
25+
```jsx
26+
<div>
27+
{showSomething === true && <Something />}
28+
{showSomethingElse === true ? (
29+
<SomethingElse />
30+
) : (
31+
<ErrorMessage />
32+
)}
33+
</div>
34+
```
35+
36+
The following patterns are **not** considered warnings:
37+
38+
```jsx
39+
<div>
40+
<Button>{data.label}</Button>
41+
42+
<List />
43+
44+
<Button>
45+
<IconPreview />
46+
Button 2
47+
48+
<span></span>
49+
</Button>
50+
51+
{showSomething === true && <Something />}
52+
53+
<Button>Button 3</Button>
54+
55+
{showSomethingElse === true ? (
56+
<SomethingElse />
57+
) : (
58+
<ErrorMessage />
59+
)}
60+
</div>
61+
```
62+
63+
## When Not To Use It
64+
65+
You can turn this rule off if you are not concerned with spacing between your JSX elements and expressions.

index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ const allRules = {
9191
'state-in-constructor': require('./lib/rules/state-in-constructor'),
9292
'static-property-placement': require('./lib/rules/static-property-placement'),
9393
'style-prop-object': require('./lib/rules/style-prop-object'),
94-
'void-dom-elements-no-children': require('./lib/rules/void-dom-elements-no-children')
94+
'void-dom-elements-no-children': require('./lib/rules/void-dom-elements-no-children'),
95+
'jsx-newline': require('./lib/rules/jsx-newline')
9596
};
9697
/* eslint-enable */
9798

lib/rules/jsx-newline.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* @fileoverview Enforce a new line after jsx elements and expressions.
3+
* @author Johnny Zabala
4+
*/
5+
6+
'use strict';
7+
8+
const arrayIncludes = require('array-includes');
9+
const docsUrl = require('../util/docsUrl');
10+
11+
// ------------------------------------------------------------------------------
12+
// Rule Definition
13+
// ------------------------------------------------------------------------------
14+
15+
module.exports = {
16+
meta: {
17+
docs: {
18+
description: 'Enforce a new line after jsx elements and expressions',
19+
category: 'Stylistic Issues',
20+
recommended: false,
21+
url: docsUrl('jsx-newline')
22+
},
23+
fixable: 'code'
24+
},
25+
create(context) {
26+
const jsxElementParents = new Set();
27+
const sourceCode = context.getSourceCode();
28+
return {
29+
'Program:exit'() {
30+
jsxElementParents.forEach((parent) => {
31+
parent.children.forEach((element, index, elements) => {
32+
if (arrayIncludes(['JSXElement', 'JSXExpressionContainer'], element.type)) {
33+
const firstAdjacentSibling = elements[index + 1];
34+
const secondAdjacentSibling = elements[index + 2];
35+
if (
36+
firstAdjacentSibling
37+
&& secondAdjacentSibling
38+
&& arrayIncludes(['Literal', 'JSXText'], firstAdjacentSibling.type)
39+
// Check adjacent sibling has the proper amount of newlines
40+
&& !/\n\s*\n/.test(firstAdjacentSibling.value)
41+
) {
42+
context.report({
43+
node: secondAdjacentSibling,
44+
message: 'JSX element should start in a new line',
45+
fix(fixer) {
46+
return fixer.replaceText(
47+
firstAdjacentSibling,
48+
sourceCode.getText(firstAdjacentSibling)
49+
.replace(/(\n)(?!.*\1)/g, '\n\n')
50+
);
51+
}
52+
});
53+
}
54+
}
55+
});
56+
});
57+
},
58+
':matches(JSXElement, JSXFragment) > :matches(JSXElement, JSXExpressionContainer)': (node) => {
59+
jsxElementParents.add(node.parent);
60+
}
61+
};
62+
}
63+
};

tests/lib/rules/jsx-newline.js

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/**
2+
* @fileoverview Enforce a new line after jsx elements and expressions
3+
* @author Johnny Zabala
4+
*/
5+
6+
'use strict';
7+
8+
// ------------------------------------------------------------------------------
9+
// Requirements
10+
// ------------------------------------------------------------------------------
11+
12+
const RuleTester = require('eslint').RuleTester;
13+
const rule = require('../../../lib/rules/jsx-newline');
14+
15+
const parsers = require('../../helpers/parsers');
16+
17+
const parserOptions = {
18+
ecmaVersion: 2018,
19+
sourceType: 'module',
20+
ecmaFeatures: {
21+
jsx: true
22+
}
23+
};
24+
25+
// ------------------------------------------------------------------------------
26+
// Tests
27+
// ------------------------------------------------------------------------------
28+
29+
const ruleTester = new RuleTester({parserOptions});
30+
ruleTester.run('jsx-newline', rule, {
31+
valid: [
32+
`
33+
<div>
34+
<Button>{data.label}</Button>
35+
36+
<List />
37+
38+
<Button>
39+
<IconPreview />
40+
Button 2
41+
42+
<span></span>
43+
</Button>
44+
45+
{showSomething === true && <Something />}
46+
47+
<Button>Button 3</Button>
48+
49+
{showSomethingElse === true ? (
50+
<SomethingElse />
51+
) : (
52+
<ErrorMessage />
53+
)}
54+
</div>
55+
`,
56+
{
57+
code: `
58+
<>
59+
<Button>{data.label}</Button>
60+
Test
61+
62+
<span>Should be in new line</span>
63+
</>
64+
`,
65+
parser: parsers.BABEL_ESLINT
66+
}
67+
],
68+
invalid: [
69+
{
70+
code: `
71+
<div>
72+
<Button>{data.label}</Button>
73+
<List />
74+
</div>
75+
`,
76+
output: `
77+
<div>
78+
<Button>{data.label}</Button>
79+
80+
<List />
81+
</div>
82+
`,
83+
errors: [{
84+
message: 'JSX element should start in a new line'
85+
}]
86+
},
87+
{
88+
code: `
89+
<div>
90+
<Button>{data.label}</Button>
91+
{showSomething === true && <Something />}
92+
</div>
93+
`,
94+
output: `
95+
<div>
96+
<Button>{data.label}</Button>
97+
98+
{showSomething === true && <Something />}
99+
</div>
100+
`,
101+
errors: [{
102+
message: 'JSX element should start in a new line'
103+
}]
104+
},
105+
{
106+
code: `
107+
<div>
108+
{showSomething === true && <Something />}
109+
<Button>{data.label}</Button>
110+
</div>
111+
`,
112+
output: `
113+
<div>
114+
{showSomething === true && <Something />}
115+
116+
<Button>{data.label}</Button>
117+
</div>
118+
`,
119+
errors: [{
120+
message: 'JSX element should start in a new line'
121+
}]
122+
},
123+
{
124+
code: `
125+
<div>
126+
{showSomething === true && <Something />}
127+
{showSomethingElse === true ? (
128+
<SomethingElse />
129+
) : (
130+
<ErrorMessage />
131+
)}
132+
</div>
133+
`,
134+
output: `
135+
<div>
136+
{showSomething === true && <Something />}
137+
138+
{showSomethingElse === true ? (
139+
<SomethingElse />
140+
) : (
141+
<ErrorMessage />
142+
)}
143+
</div>
144+
`,
145+
errors: [{
146+
message: 'JSX element should start in a new line'
147+
}]
148+
},
149+
{
150+
code: `
151+
<div>
152+
<div>
153+
<button></button>
154+
<button></button>
155+
</div>
156+
<div>
157+
<span></span>
158+
<span></span>
159+
</div>
160+
</div>
161+
`,
162+
output: `
163+
<div>
164+
<div>
165+
<button></button>
166+
167+
<button></button>
168+
</div>
169+
170+
<div>
171+
<span></span>
172+
173+
<span></span>
174+
</div>
175+
</div>
176+
`,
177+
errors: [
178+
{message: 'JSX element should start in a new line'},
179+
{message: 'JSX element should start in a new line'},
180+
{message: 'JSX element should start in a new line'}
181+
]
182+
},
183+
{
184+
code: `
185+
<>
186+
<Button>{data.label}</Button>
187+
Test
188+
<span>Should be in new line</span>
189+
</>
190+
`,
191+
output: `
192+
<>
193+
<Button>{data.label}</Button>
194+
Test
195+
196+
<span>Should be in new line</span>
197+
</>
198+
`,
199+
errors: [
200+
{message: 'JSX element should start in a new line'}
201+
],
202+
parser: parsers.BABEL_ESLINT
203+
}
204+
]
205+
});

0 commit comments

Comments
 (0)