Skip to content

Commit 316bc40

Browse files
Nokel81ljharb
authored andcommitted
[Fix] jsx-tag-spacing: change multiline-always to proportional-always
- If the node is multiline then require newline - If the node is singleline then require space Note: breaking wrt jsx-eslint#3260, but it’s unreleased. Signed-off-by: Sebastian Malton <[email protected]>
1 parent 9356230 commit 316bc40

File tree

4 files changed

+49
-28
lines changed

4 files changed

+49
-28
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
88
### Added
99
* [`destructuring-assignment`]: add option `destructureInSignature` ([#3235][] @golopot)
1010
* [`no-unknown-property`]: Allow crossOrigin on image tag (SVG) ([#3251][] @zpao)
11-
* [`jsx-tag-spacing`]: Add `multiline-always` option ([#3260][] @Nokel81)
11+
* [`jsx-tag-spacing`]: Add `multiline-always` option ([#3260][], [#3264][] @Nokel81)
1212
* [`function-component-definition`]: replace `var` by `const` in certain situations ([#3248][] @JohnBerd @SimeonC)
1313
* add [`jsx-no-leaked-render`] ([#3203][] @Belco90)
1414
* [`require-default-props`]: add option `functions` ([#3249][] @nix6839)
@@ -45,6 +45,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
4545
[#3267]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3267
4646
[#3266]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3266
4747
[#3265]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3265
48+
[#3264]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3264
4849
[#3261]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3261
4950
[#3260]: https://github.jsx-eslintckcr/eslint-plugin-react/pull/3260
5051
[#3259]: https://githubjsx-eslintickcr/eslint-plugin-react/pull/3259

lib/rules/jsx-tag-spacing.js

+25-9
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ function validateBeforeSelfClosing(context, node, option) {
101101
const leftToken = getTokenBeforeClosingBracket(node);
102102
const closingSlash = sourceCode.getTokenAfter(leftToken);
103103

104-
if (node.loc.start.line !== node.loc.end.line && option === 'multiline-always') {
104+
if (node.loc.start.line !== node.loc.end.line && option === 'proportional-always') {
105105
if (leftToken.loc.end.line === closingSlash.loc.start.line) {
106106
report(context, messages.beforeSelfCloseNeedNewline, 'beforeSelfCloseNeedNewline', {
107107
node,
@@ -110,22 +110,25 @@ function validateBeforeSelfClosing(context, node, option) {
110110
return fixer.insertTextBefore(closingSlash, '\n');
111111
},
112112
});
113+
return;
113114
}
114115
}
115116

116117
if (leftToken.loc.end.line !== closingSlash.loc.start.line) {
117118
return;
118119
}
119120

120-
if (option === 'always' && !sourceCode.isSpaceBetweenTokens(leftToken, closingSlash)) {
121+
const adjacent = !sourceCode.isSpaceBetweenTokens(leftToken, closingSlash);
122+
123+
if ((option === 'always' || option === 'proportional-always') && adjacent) {
121124
report(context, messages.beforeSelfCloseNeedSpace, 'beforeSelfCloseNeedSpace', {
122125
node,
123126
loc: closingSlash.loc.start,
124127
fix(fixer) {
125128
return fixer.insertTextBefore(closingSlash, ' ');
126129
},
127130
});
128-
} else if (option === 'never' && sourceCode.isSpaceBetweenTokens(leftToken, closingSlash)) {
131+
} else if (option === 'never' && !adjacent) {
129132
report(context, messages.beforeSelfCloseNoSpace, 'beforeSelfCloseNoSpace', {
130133
node,
131134
loc: closingSlash.loc.start,
@@ -180,11 +183,12 @@ function validateBeforeClosing(context, node, option) {
180183
// Don't enforce this rule for self closing tags
181184
if (!node.selfClosing) {
182185
const sourceCode = context.getSourceCode();
183-
const lastTokens = sourceCode.getLastTokens(node, 2);
184-
const closingToken = lastTokens[1];
185-
const leftToken = lastTokens[0];
186+
const leftToken = option === 'proportional-always'
187+
? getTokenBeforeClosingBracket(node)
188+
: sourceCode.getLastTokens(node, 2)[0];
189+
const closingToken = sourceCode.getTokenAfter(leftToken);
186190

187-
if (node.loc.start.line !== node.loc.end.line && option === 'multiline-always') {
191+
if (node.loc.start.line !== node.loc.end.line && option === 'proportional-always') {
188192
if (leftToken.loc.end.line === closingToken.loc.start.line) {
189193
report(context, messages.beforeCloseNeedNewline, 'beforeCloseNeedNewline', {
190194
node,
@@ -193,6 +197,7 @@ function validateBeforeClosing(context, node, option) {
193197
return fixer.insertTextBefore(closingToken, '\n');
194198
},
195199
});
200+
return;
196201
}
197202
}
198203

@@ -224,6 +229,17 @@ function validateBeforeClosing(context, node, option) {
224229
return fixer.insertTextBefore(closingToken, ' ');
225230
},
226231
});
232+
} else if (option === 'proportional-always' && node.type === 'JSXOpeningElement' && adjacent !== (node.loc.start.line === node.loc.end.line)) {
233+
report(context, messages.beforeCloseNeedSpace, 'beforeCloseNeedSpace', {
234+
node,
235+
loc: {
236+
start: leftToken.loc.end,
237+
end: closingToken.loc.start,
238+
},
239+
fix(fixer) {
240+
return fixer.insertTextBefore(closingToken, ' ');
241+
},
242+
});
227243
}
228244
}
229245
}
@@ -259,13 +275,13 @@ module.exports = {
259275
enum: ['always', 'never', 'allow'],
260276
},
261277
beforeSelfClosing: {
262-
enum: ['always', 'multiline-always', 'never', 'allow'],
278+
enum: ['always', 'proportional-always', 'never', 'allow'],
263279
},
264280
afterOpening: {
265281
enum: ['always', 'allow-multiline', 'never', 'allow'],
266282
},
267283
beforeClosing: {
268-
enum: ['always', 'multiline-always', 'never', 'allow'],
284+
enum: ['always', 'proportional-always', 'never', 'allow'],
269285
},
270286
},
271287
default: optionDefaults,

lib/util/getTokenBeforeClosingBracket.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88
function getTokenBeforeClosingBracket(node) {
99
const attributes = node.attributes;
10-
if (attributes.length === 0) {
10+
if (!attributes || attributes.length === 0) {
1111
return node.name;
1212
}
1313
return attributes[attributes.length - 1];

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

+21-17
Original file line numberDiff line numberDiff line change
@@ -114,21 +114,13 @@ ruleTester.run('jsx-tag-spacing', rule, {
114114
code: '<App/>',
115115
options: beforeSelfClosingOptions('never'),
116116
},
117-
{
118-
code: '<App/>',
119-
options: beforeSelfClosingOptions('multiline-always'),
120-
},
121117
{
122118
code: '<App />',
123-
options: beforeSelfClosingOptions('multiline-always'),
124-
},
125-
{
126-
code: '<App foo/>',
127-
options: beforeSelfClosingOptions('multiline-always'),
119+
options: beforeSelfClosingOptions('proportional-always'),
128120
},
129121
{
130122
code: '<App foo />',
131-
options: beforeSelfClosingOptions('multiline-always'),
123+
options: beforeSelfClosingOptions('proportional-always'),
132124
},
133125
{
134126
code: `
@@ -139,23 +131,23 @@ ruleTester.run('jsx-tag-spacing', rule, {
139131
hello
140132
</App>
141133
`,
142-
options: beforeClosingOptions('multiline-always'),
134+
options: beforeClosingOptions('proportional-always'),
143135
},
144136
{
145137
code: `
146138
<App foo={bar}>
147139
hello
148140
</App>
149141
`,
150-
options: beforeClosingOptions('multiline-always'),
142+
options: beforeClosingOptions('proportional-always'),
151143
},
152144
{
153145
code: `
154146
<App
155147
foo={bar}
156148
/>
157149
`,
158-
options: beforeSelfClosingOptions('multiline-always'),
150+
options: beforeSelfClosingOptions('proportional-always'),
159151
},
160152
{
161153
code: '<App foo/>',
@@ -345,6 +337,18 @@ ruleTester.run('jsx-tag-spacing', rule, {
345337
options: beforeSelfClosingOptions('never'),
346338
errors: [{ messageId: 'beforeSelfCloseNoSpace' }],
347339
},
340+
{
341+
code: '<App/>',
342+
output: '<App />',
343+
options: beforeSelfClosingOptions('proportional-always'),
344+
errors: [{ messageId: 'beforeSelfCloseNeedSpace' }],
345+
},
346+
{
347+
code: '<App foo/>',
348+
output: '<App foo />',
349+
options: beforeSelfClosingOptions('proportional-always'),
350+
errors: [{ messageId: 'beforeSelfCloseNeedSpace' }],
351+
},
348352
{
349353
code: `
350354
<App
@@ -353,7 +357,7 @@ ruleTester.run('jsx-tag-spacing', rule, {
353357
<App
354358
foo={bar}
355359
/>`,
356-
options: beforeSelfClosingOptions('multiline-always'),
360+
options: beforeSelfClosingOptions('proportional-always'),
357361
errors: [{ messageId: 'beforeSelfCloseNeedNewline' }],
358362
},
359363
{
@@ -364,7 +368,7 @@ ruleTester.run('jsx-tag-spacing', rule, {
364368
<App
365369
foo={bar}${' '}
366370
/>`,
367-
options: beforeSelfClosingOptions('multiline-always'),
371+
options: beforeSelfClosingOptions('proportional-always'),
368372
errors: [{ messageId: 'beforeSelfCloseNeedNewline' }],
369373
},
370374
{
@@ -383,7 +387,7 @@ ruleTester.run('jsx-tag-spacing', rule, {
383387
hello
384388
</App>
385389
`,
386-
options: beforeClosingOptions('multiline-always'),
390+
options: beforeClosingOptions('proportional-always'),
387391
errors: [{ messageId: 'beforeCloseNeedNewline' }],
388392
},
389393
{
@@ -400,7 +404,7 @@ ruleTester.run('jsx-tag-spacing', rule, {
400404
hello
401405
</App>
402406
`,
403-
options: beforeClosingOptions('multiline-always'),
407+
options: beforeClosingOptions('proportional-always'),
404408
errors: [{ messageId: 'beforeCloseNeedNewline' }],
405409
},
406410
{

0 commit comments

Comments
 (0)