Skip to content

Commit 7440098

Browse files
committed
Add multiline-always option for jsx-tag-spacing
- This option enforces that the closingTag or the selfClosingTag is on a new line if the entire tag is a multiline tag Signed-off-by: Sebastian Malton <[email protected]>
1 parent 5bc571d commit 7440098

File tree

3 files changed

+126
-3
lines changed

3 files changed

+126
-3
lines changed

lib/rules/jsx-max-props-per-line.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ module.exports = {
4848
},
4949
multi: {
5050
type: 'integer',
51-
minimum: 1,
51+
minimum: 0,
5252
},
5353
},
5454
},

lib/rules/jsx-tag-spacing.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ const messages = {
1616
closeSlashNeedSpace: 'Whitespace is required between `<` and `/`; write `< /`',
1717
beforeSelfCloseNoSpace: 'A space is forbidden before closing bracket',
1818
beforeSelfCloseNeedSpace: 'A space is required before closing bracket',
19+
beforeSelfCloseNeedNewline: 'A newline is required before closing bracket',
1920
afterOpenNoSpace: 'A space is forbidden after opening bracket',
2021
afterOpenNeedSpace: 'A space is required after opening bracket',
2122
beforeCloseNoSpace: 'A space is forbidden before closing bracket',
2223
beforeCloseNeedSpace: 'Whitespace is required before closing bracket',
24+
beforeCloseNeedNewline: 'A newline is required before closing bracket',
2325
};
2426

2527
// ------------------------------------------------------------------------------
@@ -99,6 +101,18 @@ function validateBeforeSelfClosing(context, node, option) {
99101
const leftToken = getTokenBeforeClosingBracket(node);
100102
const closingSlash = sourceCode.getTokenAfter(leftToken);
101103

104+
if (node.loc.start.line !== node.loc.end.line && option === 'multiline-always') {
105+
if (leftToken.loc.end.line === closingSlash.loc.start.line) {
106+
report(context, messages.beforeSelfCloseNeedNewline, 'beforeSelfCloseNeedNewline', {
107+
node,
108+
loc: leftToken.loc.end,
109+
fix(fixer) {
110+
return fixer.insertTextBefore(closingSlash, '\n');
111+
},
112+
});
113+
}
114+
}
115+
102116
if (leftToken.loc.end.line !== closingSlash.loc.start.line) {
103117
return;
104118
}
@@ -170,6 +184,18 @@ function validateBeforeClosing(context, node, option) {
170184
const closingToken = lastTokens[1];
171185
const leftToken = lastTokens[0];
172186

187+
if (node.loc.start.line !== node.loc.end.line && option === 'multiline-always') {
188+
if (leftToken.loc.end.line === closingToken.loc.start.line) {
189+
report(context, messages.beforeCloseNeedNewline, 'beforeCloseNeedNewline', {
190+
node,
191+
loc: leftToken.loc.end,
192+
fix(fixer) {
193+
return fixer.insertTextBefore(closingToken, '\n');
194+
},
195+
});
196+
}
197+
}
198+
173199
if (leftToken.loc.start.line !== closingToken.loc.start.line) {
174200
return;
175201
}
@@ -233,13 +259,13 @@ module.exports = {
233259
enum: ['always', 'never', 'allow'],
234260
},
235261
beforeSelfClosing: {
236-
enum: ['always', 'never', 'allow'],
262+
enum: ['always', 'multiline-always', 'never', 'allow'],
237263
},
238264
afterOpening: {
239265
enum: ['always', 'allow-multiline', 'never', 'allow'],
240266
},
241267
beforeClosing: {
242-
enum: ['always', 'never', 'allow'],
268+
enum: ['always', 'multiline-always', 'never', 'allow'],
243269
},
244270
},
245271
default: optionDefaults,

tests/lib/rules/jsx-tag-spacing.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,49 @@ ruleTester.run('jsx-tag-spacing', rule, {
114114
code: '<App/>',
115115
options: beforeSelfClosingOptions('never'),
116116
},
117+
{
118+
code: '<App/>',
119+
options: beforeSelfClosingOptions('multiline-always'),
120+
},
121+
{
122+
code: '<App />',
123+
options: beforeSelfClosingOptions('multiline-always'),
124+
},
125+
{
126+
code: '<App foo/>',
127+
options: beforeSelfClosingOptions('multiline-always'),
128+
},
129+
{
130+
code: '<App foo />',
131+
options: beforeSelfClosingOptions('multiline-always'),
132+
},
133+
{
134+
code: `
135+
<App
136+
foo={bar}
137+
blat
138+
>
139+
hello
140+
</App>
141+
`,
142+
options: beforeClosingOptions('multiline-always'),
143+
},
144+
{
145+
code: `
146+
<App foo={bar}>
147+
hello
148+
</App>
149+
`,
150+
options: beforeClosingOptions('multiline-always'),
151+
},
152+
{
153+
code: `
154+
<App
155+
foo={bar}
156+
/>
157+
`,
158+
options: beforeSelfClosingOptions('multiline-always'),
159+
},
117160
{
118161
code: '<App foo/>',
119162
options: beforeSelfClosingOptions('never'),
@@ -302,6 +345,60 @@ ruleTester.run('jsx-tag-spacing', rule, {
302345
options: beforeSelfClosingOptions('never'),
303346
errors: [{ messageId: 'beforeSelfCloseNoSpace' }],
304347
},
348+
{
349+
code: `
350+
<App
351+
foo={bar}/>`,
352+
output: `
353+
<App
354+
foo={bar}\n/>`,
355+
options: beforeSelfClosingOptions('multiline-always'),
356+
errors: [{ messageId: 'beforeSelfCloseNeedNewline' }],
357+
},
358+
{
359+
code: `
360+
<App
361+
foo={bar} />`,
362+
output: `
363+
<App
364+
foo={bar} \n/>`,
365+
options: beforeSelfClosingOptions('multiline-always'),
366+
errors: [{ messageId: 'beforeSelfCloseNeedNewline' }],
367+
},
368+
{
369+
code: `
370+
<App
371+
foo={bar}
372+
blat >
373+
hello
374+
</App>
375+
`,
376+
output: `
377+
<App
378+
foo={bar}
379+
blat \n>
380+
hello
381+
</App>
382+
`,
383+
options: beforeClosingOptions('multiline-always'),
384+
errors: [{ messageId: 'beforeCloseNeedNewline' }],
385+
},
386+
{
387+
code: `
388+
<App
389+
foo={bar}>
390+
hello
391+
</App>
392+
`,
393+
output: `
394+
<App
395+
foo={bar}\n>
396+
hello
397+
</App>
398+
`,
399+
options: beforeClosingOptions('multiline-always'),
400+
errors: [{ messageId: 'beforeCloseNeedNewline' }],
401+
},
305402
{
306403
code: '<App {...props} />',
307404
output: '<App {...props}/>',

0 commit comments

Comments
 (0)