Skip to content

Commit c624783

Browse files
committed
Fix jsx-closing-bracket-location fixer (fixes jsx-eslint#573)
1 parent 075864c commit c624783

File tree

2 files changed

+125
-12
lines changed

2 files changed

+125
-12
lines changed

lib/rules/jsx-closing-bracket-location.js

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -142,25 +142,35 @@ module.exports = function(context) {
142142
};
143143
}
144144

145-
var lastAttributeEndPos;
146-
var lastAttributeStartPos;
145+
/**
146+
* Get an unique ID for a given JSXOpeningElement
147+
*
148+
* @param {ASTNode} node The AST node being checked.
149+
* @returns {String} Unique ID (based on its range)
150+
*/
151+
function getOpeningElementId(node) {
152+
return node.range.join(':');
153+
}
154+
155+
var lastAttributeNode = {};
147156

148157
return {
149158
JSXAttribute: function(node) {
150-
lastAttributeEndPos = node.end;
151-
lastAttributeStartPos = node.start;
159+
lastAttributeNode[getOpeningElementId(node.parent)] = node;
160+
},
161+
162+
JSXSpreadAttribute: function(node) {
163+
lastAttributeNode[getOpeningElementId(node.parent)] = node;
152164
},
153165

154166
'JSXOpeningElement:exit': function(node) {
155-
var cachedLastAttributeEndPos = lastAttributeEndPos;
156-
var cachedLastAttributeStartPos = lastAttributeStartPos;
167+
var attributeNode = lastAttributeNode[getOpeningElementId(node)];
168+
var cachedLastAttributeEndPos = attributeNode ? attributeNode.end : null;
169+
var cachedLastAttributeStartPos = attributeNode ? attributeNode.start : null;
157170
var expectedNextLine;
158171
var tokens = getTokensLocations(node);
159172
var expectedLocation = getExpectedLocation(tokens);
160173

161-
lastAttributeStartPos = null;
162-
lastAttributeEndPos = null;
163-
164174
if (hasCorrectLocation(tokens, expectedLocation)) {
165175
return;
166176
}
@@ -188,8 +198,8 @@ module.exports = function(context) {
188198
return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end],
189199
(expectedNextLine ? '\n' : '') + closingTag);
190200
}
191-
return fixer.replaceTextRange([node.name.loc.end.column + 1, node.end],
192-
(expectedNextLine ? '\n' : '') + closingTag);
201+
return fixer.replaceTextRange([node.name.range[1], node.end],
202+
(expectedNextLine ? '\n' : ' ') + closingTag);
193203
case 'after-props':
194204
return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end],
195205
(expectedNextLine ? '\n' : '') + closingTag);
@@ -198,7 +208,7 @@ module.exports = function(context) {
198208
return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end],
199209
'\n' + spaces.join(' ') + closingTag);
200210
case 'tag-aligned':
201-
var tagSpaces = new Array(node.start);
211+
var tagSpaces = new Array(+correctColumn + 1);
202212
return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end],
203213
'\n' + tagSpaces.join(' ') + closingTag);
204214
case 'line-aligned':

tests/lib/rules/jsx-closing-bracket-location.js

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,11 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
564564
' foo',
565565
' ></App>'
566566
].join('\n'),
567+
output: [
568+
'<App',
569+
' foo',
570+
'></App>'
571+
].join('\n'),
567572
options: [{location: 'tag-aligned'}],
568573
parserOptions: parserOptions,
569574
errors: [{
@@ -577,6 +582,11 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
577582
' foo',
578583
' ></App>'
579584
].join('\n'),
585+
output: [
586+
'<App',
587+
' foo',
588+
'></App>'
589+
].join('\n'),
580590
options: [{location: 'line-aligned'}],
581591
parserOptions: parserOptions,
582592
errors: [{
@@ -593,6 +603,15 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
593603
' />',
594604
'</Provider>'
595605
].join('\n'),
606+
output: [
607+
'<Provider ',
608+
' store',
609+
'>',
610+
' <App ',
611+
' foo',
612+
' />',
613+
'</Provider>'
614+
].join('\n'),
596615
options: [{selfClosing: 'props-aligned'}],
597616
parserOptions: parserOptions,
598617
errors: [{
@@ -610,6 +629,15 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
610629
' />', // <--
611630
'</Provider>'
612631
].join('\n'),
632+
output: [
633+
'<Provider',
634+
' store',
635+
' >',
636+
' <App ',
637+
' foo',
638+
' />',
639+
'</Provider>'
640+
].join('\n'),
613641
options: [{nonEmpty: 'props-aligned'}],
614642
parserOptions: parserOptions,
615643
errors: [{
@@ -625,6 +653,14 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
625653
' foo />',
626654
'</Provider>'
627655
].join('\n'),
656+
output: [
657+
'<Provider ',
658+
' store',
659+
'>',
660+
' <App',
661+
' foo />',
662+
'</Provider>'
663+
].join('\n'),
628664
options: [{selfClosing: 'after-props'}],
629665
parserOptions: parserOptions,
630666
errors: [{
@@ -641,6 +677,14 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
641677
' />', // <--
642678
'</Provider>'
643679
].join('\n'),
680+
output: [
681+
'<Provider ',
682+
' store>',
683+
' <App ',
684+
' foo',
685+
' />', // <--
686+
'</Provider>'
687+
].join('\n'),
644688
options: [{nonEmpty: 'after-props'}],
645689
parserOptions: parserOptions,
646690
errors: [{
@@ -688,5 +732,64 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
688732
line: 3,
689733
column: 9
690734
}]
735+
}, {
736+
code: [
737+
'var x = (',
738+
' <div',
739+
' className="MyComponent"',
740+
' {...props} />',
741+
')'
742+
].join('\n'),
743+
output: [
744+
'var x = (',
745+
' <div',
746+
' className="MyComponent"',
747+
' {...props}',
748+
' />',
749+
')'
750+
].join('\n'),
751+
options: [{location: 'line-aligned'}],
752+
parserOptions: parserOptions,
753+
errors: [{
754+
message: messageWithDetails(MESSAGE_LINE_ALIGNED, 3, true),
755+
line: 4,
756+
column: 16
757+
}]
758+
}, {
759+
code: [
760+
'var x = (',
761+
' <Something',
762+
' content={<Foo />} />',
763+
')'
764+
].join('\n'),
765+
output: [
766+
'var x = (',
767+
' <Something',
768+
' content={<Foo />}',
769+
' />',
770+
')'
771+
].join('\n'),
772+
options: [{location: 'line-aligned'}],
773+
parserOptions: parserOptions,
774+
errors: [{
775+
message: messageWithDetails(MESSAGE_LINE_ALIGNED, 3, true),
776+
line: 3,
777+
column: 23
778+
}]
779+
}, {
780+
code: [
781+
'var x = (',
782+
' <Something ',
783+
' />',
784+
')'
785+
].join('\n'),
786+
output: [
787+
'var x = (',
788+
' <Something />',
789+
')'
790+
].join('\n'),
791+
options: [{location: 'line-aligned'}],
792+
parserOptions: parserOptions,
793+
errors: [MESSAGE_AFTER_TAG]
691794
}]
692795
});

0 commit comments

Comments
 (0)