Skip to content

Commit 12e9838

Browse files
justmejulianljharb
authored andcommitted
[Fix] jsx-newline: No newline between comments and jsx elements
1 parent 54c56a1 commit 12e9838

File tree

3 files changed

+171
-1
lines changed

3 files changed

+171
-1
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
1515
* configs: avoid legacy config system error ([#3461][] @ljharb)
1616
* [`sort-prop-types`]: restore autofixing ([#3452][] @ROSSROSALES)
1717
* [`no-unknown-property`]: do not check `fbs` elements ([#3494][] @brianogilvie)
18+
* [`jsx-newline`]: No newline between comments and jsx elements ([#3493][] @justmejulian)
1819

1920
[#3494]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3494
21+
[#3493]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3493
2022
[#3461]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3461
2123
[#3452]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3452
2224
[#3449]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3449

lib/rules/jsx-newline.js

+19-1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@ module.exports = {
7272
const jsxElementParents = new Set();
7373
const sourceCode = context.getSourceCode();
7474

75+
function isBlockCommentInCurlyBraces(element) {
76+
const elementRawValue = sourceCode.getText(element);
77+
return /^\s*{\/\*/.test(elementRawValue);
78+
}
79+
80+
function isNonBlockComment(element) {
81+
return !isBlockCommentInCurlyBraces(element) && (element.type === 'JSXElement' || element.type === 'JSXExpressionContainer');
82+
}
83+
7584
return {
7685
'Program:exit'() {
7786
jsxElementParents.forEach((parent) => {
@@ -93,7 +102,15 @@ module.exports = {
93102
// Check adjacent sibling has the proper amount of newlines
94103
const isWithoutNewLine = !/\n\s*\n/.test(firstAdjacentSibling.value);
95104

96-
if (allowMultilines && (isMultilined(element) || isMultilined(secondAdjacentSibling))) {
105+
if (isBlockCommentInCurlyBraces(element)) return;
106+
107+
if (
108+
allowMultilines
109+
&& (
110+
isMultilined(element)
111+
|| isMultilined(elements.slice(index + 2).find(isNonBlockComment))
112+
)
113+
) {
97114
if (!isWithoutNewLine) return;
98115

99116
const regex = /(\n)(?!.*\1)/g;
@@ -115,6 +132,7 @@ module.exports = {
115132
}
116133

117134
if (isWithoutNewLine === prevent) return;
135+
118136
const messageId = prevent
119137
? 'prevent'
120138
: 'require';

tests/lib/rules/jsx-newline.js

+150
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,44 @@ new RuleTester({ parserOptions }).run('jsx-newline', rule, {
105105
</Button>
106106
`,
107107
},
108+
{
109+
code: `
110+
<Button popoverOpen='#settings-popover' style={{ width: 'fit-content' }}>
111+
{/* fake-eslint-disable-next-line should also work inside a component */}
112+
<Icon f7='gear' />
113+
</Button>
114+
`,
115+
},
116+
{
117+
code: `
118+
<Button popoverOpen='#settings-popover' style={{ width: 'fit-content' }}>
119+
{/* should work inside a component */}
120+
{/* and it should work when using multiple comments */}
121+
<Icon f7='gear' />
122+
</Button>
123+
`,
124+
},
125+
{
126+
code: `
127+
<Button popoverOpen='#settings-popover' style={{ width: 'fit-content' }}>
128+
{/* this is a multiline
129+
block comment */}
130+
<Icon f7='gear' />
131+
</Button>
132+
`,
133+
},
134+
{
135+
code: `
136+
<>
137+
{/* does this */}
138+
<Icon f7='gear' />
139+
140+
{/* also work with multiple components and inside a fragment? */}
141+
<OneLineComponent />
142+
</>
143+
`,
144+
features: ['fragment'],
145+
},
108146
{
109147
code: `
110148
<>
@@ -122,6 +160,24 @@ new RuleTester({ parserOptions }).run('jsx-newline', rule, {
122160
features: ['fragment'],
123161
options: [{ prevent: true, allowMultilines: true }],
124162
},
163+
{
164+
code: `
165+
<div>
166+
{/* this does not have a newline */}
167+
<Icon f7='gear' />
168+
{/* neither does this */}
169+
<OneLineComponent />
170+
171+
{/* but this one needs one */}
172+
<Button>
173+
<IconPreview />
174+
Button 2
175+
<span></span>
176+
</Button>
177+
</div>
178+
`,
179+
options: [{ prevent: true, allowMultilines: true }],
180+
},
125181
{
126182
code: `
127183
<div>
@@ -223,6 +279,100 @@ new RuleTester({ parserOptions }).run('jsx-newline', rule, {
223279
`,
224280
errors: [{ messageId: 'require' }],
225281
},
282+
{
283+
code: `
284+
<div>
285+
{/* This should however still not work*/}
286+
<Icon f7='gear' />
287+
288+
<OneLineComponent />
289+
{/* Comments between components still need a newLine */}
290+
<OneLineComponent />
291+
</div>
292+
`,
293+
output: `
294+
<div>
295+
{/* This should however still not work*/}
296+
<Icon f7='gear' />
297+
298+
<OneLineComponent />
299+
300+
{/* Comments between components still need a newLine */}
301+
<OneLineComponent />
302+
</div>
303+
`,
304+
errors: [{ messageId: 'require' }],
305+
},
306+
{
307+
code: `
308+
<div>
309+
{/* this does not have a newline */}
310+
<Icon f7='gear' />
311+
{/* neither does this */}
312+
<OneLineComponent />
313+
{/* but this one needs one */}
314+
<Button>
315+
<IconPreview />
316+
Button 2
317+
<span></span>
318+
</Button>
319+
</div>
320+
`,
321+
output: `
322+
<div>
323+
{/* this does not have a newline */}
324+
<Icon f7='gear' />
325+
{/* neither does this */}
326+
<OneLineComponent />
327+
328+
{/* but this one needs one */}
329+
<Button>
330+
<IconPreview />
331+
Button 2
332+
<span></span>
333+
</Button>
334+
</div>
335+
`,
336+
options: [{ prevent: true, allowMultilines: true }],
337+
errors: [{ messageId: 'allowMultilines' }],
338+
},
339+
{
340+
code: `
341+
<div>
342+
{/* this does not have a newline */}
343+
<Icon f7='gear' />
344+
{/* neither does this */}
345+
<OneLineComponent />
346+
{/* Multiline */}
347+
{/* Block comments */}
348+
{/* Stick to MultilineComponent */}
349+
<Button>
350+
<IconPreview />
351+
Button 2
352+
<span></span>
353+
</Button>
354+
</div>
355+
`,
356+
output: `
357+
<div>
358+
{/* this does not have a newline */}
359+
<Icon f7='gear' />
360+
{/* neither does this */}
361+
<OneLineComponent />
362+
363+
{/* Multiline */}
364+
{/* Block comments */}
365+
{/* Stick to MultilineComponent */}
366+
<Button>
367+
<IconPreview />
368+
Button 2
369+
<span></span>
370+
</Button>
371+
</div>
372+
`,
373+
options: [{ prevent: true, allowMultilines: true }],
374+
errors: [{ messageId: 'allowMultilines' }],
375+
},
226376
{
227377
code: `
228378
<div>

0 commit comments

Comments
 (0)