diff --git a/lib/rules/jsx-tag-spacing.js b/lib/rules/jsx-tag-spacing.js
index 9db4a8923a..6ff21b7e05 100644
--- a/lib/rules/jsx-tag-spacing.js
+++ b/lib/rules/jsx-tag-spacing.js
@@ -101,9 +101,9 @@ function validateBeforeSelfClosing(context, node, option) {
const leftToken = getTokenBeforeClosingBracket(node);
const closingSlash = sourceCode.getTokenAfter(leftToken);
- if (node.loc.start.line !== node.loc.end.line && option === 'multiline-always') {
+ if (node.loc.start.line !== node.loc.end.line && option === 'proportional-always') {
if (leftToken.loc.end.line === closingSlash.loc.start.line) {
- report(context, messages.beforeSelfCloseNeedNewline, 'beforeSelfCloseNeedNewline', {
+ return report(context, messages.beforeSelfCloseNeedNewline, 'beforeSelfCloseNeedNewline', {
node,
loc: leftToken.loc.end,
fix(fixer) {
@@ -117,7 +117,9 @@ function validateBeforeSelfClosing(context, node, option) {
return;
}
- if (option === 'always' && !sourceCode.isSpaceBetweenTokens(leftToken, closingSlash)) {
+ const adjacent = !sourceCode.isSpaceBetweenTokens(leftToken, closingSlash);
+
+ if ((option === 'always' || option === 'proportional-always') && adjacent) {
report(context, messages.beforeSelfCloseNeedSpace, 'beforeSelfCloseNeedSpace', {
node,
loc: closingSlash.loc.start,
@@ -125,7 +127,7 @@ function validateBeforeSelfClosing(context, node, option) {
return fixer.insertTextBefore(closingSlash, ' ');
},
});
- } else if (option === 'never' && sourceCode.isSpaceBetweenTokens(leftToken, closingSlash)) {
+ } else if (option === 'never' && !adjacent) {
report(context, messages.beforeSelfCloseNoSpace, 'beforeSelfCloseNoSpace', {
node,
loc: closingSlash.loc.start,
@@ -178,54 +180,68 @@ function validateAfterOpening(context, node, option) {
function validateBeforeClosing(context, node, option) {
// Don't enforce this rule for self closing tags
- if (!node.selfClosing) {
- const sourceCode = context.getSourceCode();
- const lastTokens = sourceCode.getLastTokens(node, 2);
- const closingToken = lastTokens[1];
- const leftToken = lastTokens[0];
-
- if (node.loc.start.line !== node.loc.end.line && option === 'multiline-always') {
- if (leftToken.loc.end.line === closingToken.loc.start.line) {
- report(context, messages.beforeCloseNeedNewline, 'beforeCloseNeedNewline', {
- node,
- loc: leftToken.loc.end,
- fix(fixer) {
- return fixer.insertTextBefore(closingToken, '\n');
- },
- });
- }
- }
-
- if (leftToken.loc.start.line !== closingToken.loc.start.line) {
- return;
- }
+ if (node.selfClosing) {
+ return;
+ }
- const adjacent = !sourceCode.isSpaceBetweenTokens(leftToken, closingToken);
+ const sourceCode = context.getSourceCode();
+ const leftToken = option === 'proportional-always'
+ ? getTokenBeforeClosingBracket(node)
+ : sourceCode.getLastTokens(node, 2)[0];
+ const closingToken = sourceCode.getTokenAfter(leftToken);
- if (option === 'never' && !adjacent) {
- report(context, messages.beforeCloseNoSpace, 'beforeCloseNoSpace', {
+ if (node.loc.start.line !== node.loc.end.line && option === 'proportional-always') {
+ if (leftToken.loc.end.line === closingToken.loc.start.line) {
+ return report(context, messages.beforeCloseNeedNewline, 'beforeCloseNeedNewline', {
node,
- loc: {
- start: leftToken.loc.end,
- end: closingToken.loc.start,
- },
- fix(fixer) {
- return fixer.removeRange([leftToken.range[1], closingToken.range[0]]);
- },
- });
- } else if (option === 'always' && adjacent) {
- report(context, messages.beforeCloseNeedSpace, 'beforeCloseNeedSpace', {
- node,
- loc: {
- start: leftToken.loc.end,
- end: closingToken.loc.start,
- },
+ loc: leftToken.loc.end,
fix(fixer) {
- return fixer.insertTextBefore(closingToken, ' ');
+ return fixer.insertTextBefore(closingToken, '\n');
},
});
}
}
+
+ if (leftToken.loc.start.line !== closingToken.loc.start.line) {
+ return;
+ }
+
+ const adjacent = !sourceCode.isSpaceBetweenTokens(leftToken, closingToken);
+
+ if (option === 'never' && !adjacent) {
+ report(context, messages.beforeCloseNoSpace, 'beforeCloseNoSpace', {
+ node,
+ loc: {
+ start: leftToken.loc.end,
+ end: closingToken.loc.start,
+ },
+ fix(fixer) {
+ return fixer.removeRange([leftToken.range[1], closingToken.range[0]]);
+ },
+ });
+ } else if (option === 'always' && adjacent) {
+ report(context, messages.beforeCloseNeedSpace, 'beforeCloseNeedSpace', {
+ node,
+ loc: {
+ start: leftToken.loc.end,
+ end: closingToken.loc.start,
+ },
+ fix(fixer) {
+ return fixer.insertTextBefore(closingToken, ' ');
+ },
+ });
+ } else if (option === 'proportional-always' && node.type === 'JSXOpeningElement' && adjacent !== (node.loc.start.line === node.loc.end.line)) {
+ report(context, messages.beforeCloseNeedSpace, 'beforeCloseNeedSpace', {
+ node,
+ loc: {
+ start: leftToken.loc.end,
+ end: closingToken.loc.start,
+ },
+ fix(fixer) {
+ return fixer.insertTextBefore(closingToken, ' ');
+ },
+ });
+ }
}
// ------------------------------------------------------------------------------
@@ -259,13 +275,13 @@ module.exports = {
enum: ['always', 'never', 'allow'],
},
beforeSelfClosing: {
- enum: ['always', 'multiline-always', 'never', 'allow'],
+ enum: ['always', 'proportional-always', 'never', 'allow'],
},
afterOpening: {
enum: ['always', 'allow-multiline', 'never', 'allow'],
},
beforeClosing: {
- enum: ['always', 'multiline-always', 'never', 'allow'],
+ enum: ['always', 'proportional-always', 'never', 'allow'],
},
},
default: optionDefaults,
diff --git a/lib/util/getTokenBeforeClosingBracket.js b/lib/util/getTokenBeforeClosingBracket.js
index a727c1a7b1..8bd277a249 100644
--- a/lib/util/getTokenBeforeClosingBracket.js
+++ b/lib/util/getTokenBeforeClosingBracket.js
@@ -7,7 +7,7 @@
*/
function getTokenBeforeClosingBracket(node) {
const attributes = node.attributes;
- if (attributes.length === 0) {
+ if (!attributes || attributes.length === 0) {
return node.name;
}
return attributes[attributes.length - 1];
diff --git a/tests/lib/rules/jsx-tag-spacing.js b/tests/lib/rules/jsx-tag-spacing.js
index e2a9c759a2..039591a7f5 100644
--- a/tests/lib/rules/jsx-tag-spacing.js
+++ b/tests/lib/rules/jsx-tag-spacing.js
@@ -114,21 +114,13 @@ ruleTester.run('jsx-tag-spacing', rule, {
code: '',
options: beforeSelfClosingOptions('never'),
},
- {
- code: '',
- options: beforeSelfClosingOptions('multiline-always'),
- },
{
code: '',
- options: beforeSelfClosingOptions('multiline-always'),
- },
- {
- code: '',
- options: beforeSelfClosingOptions('multiline-always'),
+ options: beforeSelfClosingOptions('proportional-always'),
},
{
code: '',
- options: beforeSelfClosingOptions('multiline-always'),
+ options: beforeSelfClosingOptions('proportional-always'),
},
{
code: `
@@ -139,7 +131,7 @@ ruleTester.run('jsx-tag-spacing', rule, {
hello
`,
- options: beforeClosingOptions('multiline-always'),
+ options: beforeClosingOptions('proportional-always'),
},
{
code: `
@@ -147,7 +139,7 @@ ruleTester.run('jsx-tag-spacing', rule, {
hello
`,
- options: beforeClosingOptions('multiline-always'),
+ options: beforeClosingOptions('proportional-always'),
},
{
code: `
@@ -155,7 +147,7 @@ ruleTester.run('jsx-tag-spacing', rule, {
foo={bar}
/>
`,
- options: beforeSelfClosingOptions('multiline-always'),
+ options: beforeSelfClosingOptions('proportional-always'),
},
{
code: '',
@@ -345,6 +337,18 @@ ruleTester.run('jsx-tag-spacing', rule, {
options: beforeSelfClosingOptions('never'),
errors: [{ messageId: 'beforeSelfCloseNoSpace' }],
},
+ {
+ code: '',
+ output: '',
+ options: beforeSelfClosingOptions('proportional-always'),
+ errors: [{ messageId: 'beforeSelfCloseNeedSpace' }],
+ },
+ {
+ code: '',
+ output: '',
+ options: beforeSelfClosingOptions('proportional-always'),
+ errors: [{ messageId: 'beforeSelfCloseNeedSpace' }],
+ },
{
code: `
`,
- options: beforeSelfClosingOptions('multiline-always'),
+ options: beforeSelfClosingOptions('proportional-always'),
errors: [{ messageId: 'beforeSelfCloseNeedNewline' }],
},
{
@@ -364,7 +368,7 @@ ruleTester.run('jsx-tag-spacing', rule, {
`,
- options: beforeSelfClosingOptions('multiline-always'),
+ options: beforeSelfClosingOptions('proportional-always'),
errors: [{ messageId: 'beforeSelfCloseNeedNewline' }],
},
{
@@ -383,7 +387,7 @@ ruleTester.run('jsx-tag-spacing', rule, {
hello
`,
- options: beforeClosingOptions('multiline-always'),
+ options: beforeClosingOptions('proportional-always'),
errors: [{ messageId: 'beforeCloseNeedNewline' }],
},
{
@@ -400,7 +404,7 @@ ruleTester.run('jsx-tag-spacing', rule, {
hello
`,
- options: beforeClosingOptions('multiline-always'),
+ options: beforeClosingOptions('proportional-always'),
errors: [{ messageId: 'beforeCloseNeedNewline' }],
},
{