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' }], }, {