diff --git a/lib/rules/jsx-closing-bracket-location.js b/lib/rules/jsx-closing-bracket-location.js index f4959cc124..50a9e1f1b7 100644 --- a/lib/rules/jsx-closing-bracket-location.js +++ b/lib/rules/jsx-closing-bracket-location.js @@ -142,25 +142,35 @@ module.exports = function(context) { }; } - var lastAttributeEndPos; - var lastAttributeStartPos; + /** + * Get an unique ID for a given JSXOpeningElement + * + * @param {ASTNode} node The AST node being checked. + * @returns {String} Unique ID (based on its range) + */ + function getOpeningElementId(node) { + return node.range.join(':'); + } + + var lastAttributeNode = {}; return { JSXAttribute: function(node) { - lastAttributeEndPos = node.end; - lastAttributeStartPos = node.start; + lastAttributeNode[getOpeningElementId(node.parent)] = node; + }, + + JSXSpreadAttribute: function(node) { + lastAttributeNode[getOpeningElementId(node.parent)] = node; }, 'JSXOpeningElement:exit': function(node) { - var cachedLastAttributeEndPos = lastAttributeEndPos; - var cachedLastAttributeStartPos = lastAttributeStartPos; + var attributeNode = lastAttributeNode[getOpeningElementId(node)]; + var cachedLastAttributeEndPos = attributeNode ? attributeNode.end : null; + var cachedLastAttributeStartPos = attributeNode ? attributeNode.start : null; var expectedNextLine; var tokens = getTokensLocations(node); var expectedLocation = getExpectedLocation(tokens); - lastAttributeStartPos = null; - lastAttributeEndPos = null; - if (hasCorrectLocation(tokens, expectedLocation)) { return; } @@ -188,8 +198,8 @@ module.exports = function(context) { return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end], (expectedNextLine ? '\n' : '') + closingTag); } - return fixer.replaceTextRange([node.name.loc.end.column + 1, node.end], - (expectedNextLine ? '\n' : '') + closingTag); + return fixer.replaceTextRange([node.name.range[1], node.end], + (expectedNextLine ? '\n' : ' ') + closingTag); case 'after-props': return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end], (expectedNextLine ? '\n' : '') + closingTag); @@ -198,7 +208,7 @@ module.exports = function(context) { return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end], '\n' + spaces.join(' ') + closingTag); case 'tag-aligned': - var tagSpaces = new Array(node.start); + var tagSpaces = new Array(+correctColumn + 1); return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end], '\n' + tagSpaces.join(' ') + closingTag); case 'line-aligned': diff --git a/tests/lib/rules/jsx-closing-bracket-location.js b/tests/lib/rules/jsx-closing-bracket-location.js index 8c4e374235..e28f25053e 100644 --- a/tests/lib/rules/jsx-closing-bracket-location.js +++ b/tests/lib/rules/jsx-closing-bracket-location.js @@ -564,6 +564,11 @@ ruleTester.run('jsx-closing-bracket-location', rule, { ' foo', ' >' ].join('\n'), + output: [ + '' + ].join('\n'), options: [{location: 'tag-aligned'}], parserOptions: parserOptions, errors: [{ @@ -577,6 +582,11 @@ ruleTester.run('jsx-closing-bracket-location', rule, { ' foo', ' >' ].join('\n'), + output: [ + '' + ].join('\n'), options: [{location: 'line-aligned'}], parserOptions: parserOptions, errors: [{ @@ -593,6 +603,15 @@ ruleTester.run('jsx-closing-bracket-location', rule, { ' />', '' ].join('\n'), + output: [ + '', + ' ', + '' + ].join('\n'), options: [{selfClosing: 'props-aligned'}], parserOptions: parserOptions, errors: [{ @@ -610,6 +629,15 @@ ruleTester.run('jsx-closing-bracket-location', rule, { ' />', // <-- '' ].join('\n'), + output: [ + '', + ' ', + '' + ].join('\n'), options: [{nonEmpty: 'props-aligned'}], parserOptions: parserOptions, errors: [{ @@ -625,6 +653,14 @@ ruleTester.run('jsx-closing-bracket-location', rule, { ' foo />', '' ].join('\n'), + output: [ + '', + ' ', + '' + ].join('\n'), options: [{selfClosing: 'after-props'}], parserOptions: parserOptions, errors: [{ @@ -641,6 +677,14 @@ ruleTester.run('jsx-closing-bracket-location', rule, { ' />', // <-- '' ].join('\n'), + output: [ + '', + ' ', // <-- + '' + ].join('\n'), options: [{nonEmpty: 'after-props'}], parserOptions: parserOptions, errors: [{ @@ -688,5 +732,64 @@ ruleTester.run('jsx-closing-bracket-location', rule, { line: 3, column: 9 }] + }, { + code: [ + 'var x = (', + ' ', + ')' + ].join('\n'), + output: [ + 'var x = (', + ' ', + ')' + ].join('\n'), + options: [{location: 'line-aligned'}], + parserOptions: parserOptions, + errors: [{ + message: messageWithDetails(MESSAGE_LINE_ALIGNED, 3, true), + line: 4, + column: 16 + }] + }, { + code: [ + 'var x = (', + ' } />', + ')' + ].join('\n'), + output: [ + 'var x = (', + ' }', + ' />', + ')' + ].join('\n'), + options: [{location: 'line-aligned'}], + parserOptions: parserOptions, + errors: [{ + message: messageWithDetails(MESSAGE_LINE_ALIGNED, 3, true), + line: 3, + column: 23 + }] + }, { + code: [ + 'var x = (', + ' ', + ')' + ].join('\n'), + output: [ + 'var x = (', + ' ', + ')' + ].join('\n'), + options: [{location: 'line-aligned'}], + parserOptions: parserOptions, + errors: [MESSAGE_AFTER_TAG] }] });