Skip to content

Commit

Permalink
Autofix for jsx-closing-bracket-location.
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinGrandon committed Mar 28, 2016
1 parent e3c27f9 commit deb8e4e
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 3 deletions.
60 changes: 57 additions & 3 deletions lib/rules/jsx-closing-bracket-location.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,25 @@ module.exports = function(context) {
};
}

var lastAttributeEndPos;
var lastAttributeStartPos;

return {
JSXOpeningElement: function(node) {
JSXAttribute: function(node) {
lastAttributeEndPos = node.end;
lastAttributeStartPos = node.start;
},

'JSXOpeningElement:exit': function(node) {
var cachedLastAttributeEndPos = lastAttributeEndPos;
var cachedLastAttributeStartPos = lastAttributeStartPos;
var expectedNextLine;
var tokens = getTokensLocations(node);
var expectedLocation = getExpectedLocation(tokens);

lastAttributeStartPos = null;
lastAttributeEndPos = null;

if (hasCorrectLocation(tokens, expectedLocation)) {
return;
}
Expand All @@ -154,7 +169,7 @@ module.exports = function(context) {
var correctColumn = getCorrectColumn(tokens, expectedLocation);

if (correctColumn !== null) {
var expectedNextLine = tokens.lastProp &&
expectedNextLine = tokens.lastProp &&
(tokens.lastProp.line === tokens.closing.line);
data.details = ' (expected column ' + (correctColumn + 1) +
(expectedNextLine ? ' on the next line)' : ')');
Expand All @@ -164,7 +179,46 @@ module.exports = function(context) {
node: node,
loc: tokens.closing,
message: MESSAGE,
data: data
data: data,
fix: function(fixer) {
var closingTag = tokens.selfClosing ? '/>' : '>';
switch (expectedLocation) {
case 'after-tag':
if (cachedLastAttributeEndPos) {
return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end],
(expectedNextLine ? '\n' : '') + closingTag);
}
return fixer.replaceTextRange([node.name.loc.end.column + 1, node.end],
(expectedNextLine ? '\n' : '') + closingTag);
case 'after-props':
return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end],
(expectedNextLine ? '\n' : '') + closingTag);
case 'props-aligned':
var spaces = new Array(cachedLastAttributeEndPos - cachedLastAttributeStartPos);
return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end],
'\n' + spaces.join(' ') + closingTag);
case 'tag-aligned':
var tagSpaces = new Array(node.start);
return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end],
'\n' + tagSpaces.join(' ') + closingTag);
case 'line-aligned':
var walkNode = node;
var lineSpaces = 0;
while ((walkNode = walkNode.parent)) {
if (walkNode.type === 'VariableDeclaration' ||
walkNode.type === 'ReturnStatement' ||
walkNode.type === 'ExpressionStatement') {
lineSpaces = walkNode.loc.start.column + 1;
break;
}
}
lineSpaces = new Array(lineSpaces);
return fixer.replaceTextRange([cachedLastAttributeEndPos, node.end],
'\n' + lineSpaces.join(' ') + closingTag);
default:
return true;
}
}
});
}
};
Expand Down
72 changes: 72 additions & 0 deletions tests/lib/rules/jsx-closing-bracket-location.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,27 +358,41 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
'<App ',
'/>'
].join('\n'),
output: [
'<App />'
].join('\n'),
parserOptions: parserOptions,
errors: MESSAGE_AFTER_TAG
}, {
code: [
'<App foo ',
'/>'
].join('\n'),
output: [
'<App foo/>'
].join('\n'),
parserOptions: parserOptions,
errors: MESSAGE_AFTER_PROPS
}, {
code: [
'<App foo',
'></App>'
].join('\n'),
output: [
'<App foo></App>'
].join('\n'),
parserOptions: parserOptions,
errors: MESSAGE_AFTER_PROPS
}, {
code: [
'<App ',
' foo />'
].join('\n'),
output: [
'<App ',
' foo',
' />'
].join('\n'),
options: [{location: 'props-aligned'}],
parserOptions: parserOptions,
errors: [{
Expand All @@ -391,6 +405,11 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
'<App ',
' foo />'
].join('\n'),
output: [
'<App ',
' foo',
'/>'
].join('\n'),
options: [{location: 'tag-aligned'}],
parserOptions: parserOptions,
errors: [{
Expand All @@ -403,6 +422,11 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
'<App ',
' foo />'
].join('\n'),
output: [
'<App ',
' foo',
'/>'
].join('\n'),
options: [{location: 'line-aligned'}],
parserOptions: parserOptions,
errors: [{
Expand All @@ -416,6 +440,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
' foo',
'/>'
].join('\n'),
output: [
'<App ',
' foo/>'
].join('\n'),
options: [{location: 'after-props'}],
parserOptions: parserOptions,
errors: MESSAGE_AFTER_PROPS
Expand All @@ -425,6 +453,11 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
' foo',
'/>'
].join('\n'),
output: [
'<App ',
' foo',
' />'
].join('\n'),
options: [{location: 'props-aligned'}],
parserOptions: parserOptions,
errors: [{
Expand All @@ -438,6 +471,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
' foo',
' />'
].join('\n'),
output: [
'<App ',
' foo/>'
].join('\n'),
options: [{location: 'after-props'}],
parserOptions: parserOptions,
errors: MESSAGE_AFTER_PROPS
Expand All @@ -447,6 +484,11 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
' foo',
' />'
].join('\n'),
output: [
'<App ',
' foo',
'/>'
].join('\n'),
options: [{location: 'tag-aligned'}],
parserOptions: parserOptions,
errors: [{
Expand All @@ -460,6 +502,11 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
' foo',
' />'
].join('\n'),
output: [
'<App ',
' foo',
'/>'
].join('\n'),
options: [{location: 'line-aligned'}],
parserOptions: parserOptions,
errors: [{
Expand All @@ -473,6 +520,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
' foo',
'></App>'
].join('\n'),
output: [
'<App',
' foo></App>'
].join('\n'),
options: [{location: 'after-props'}],
parserOptions: parserOptions,
errors: MESSAGE_AFTER_PROPS
Expand All @@ -482,6 +533,11 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
' foo',
'></App>'
].join('\n'),
output: [
'<App',
' foo',
' ></App>'
].join('\n'),
options: [{location: 'props-aligned'}],
parserOptions: parserOptions,
errors: [{
Expand All @@ -495,6 +551,10 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
' foo',
' ></App>'
].join('\n'),
output: [
'<App',
' foo></App>'
].join('\n'),
options: [{location: 'after-props'}],
parserOptions: parserOptions,
errors: MESSAGE_AFTER_PROPS
Expand Down Expand Up @@ -596,6 +656,13 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
' />',
'}'
].join('\n'),
output: [
'var x = function() {',
' return <App',
' foo',
' />',
'}'
].join('\n'),
options: [{location: 'line-aligned'}],
parserOptions: parserOptions,
errors: [{
Expand All @@ -609,6 +676,11 @@ ruleTester.run('jsx-closing-bracket-location', rule, {
' foo',
' />'
].join('\n'),
output: [
'var x = <App',
' foo',
'/>'
].join('\n'),
options: [{location: 'line-aligned'}],
parserOptions: parserOptions,
errors: [{
Expand Down

0 comments on commit deb8e4e

Please sign in to comment.