Skip to content

Commit

Permalink
[Fix] jsx-indent with tabs (fixes jsx-eslint#1057)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kent C. Dodds committed Feb 1, 2017
1 parent c97dd0f commit 2141cb5
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 27 deletions.
60 changes: 34 additions & 26 deletions lib/rules/jsx-indent.js
Expand Up @@ -154,7 +154,8 @@ module.exports = {
}

/**
* Checks node is the first in its own start line. By default it looks by start line.
* Checks if the node is the first in its own start line. By default it looks by start line.
* One exception is closing tags with preceeding whitespace
* @param {ASTNode} node The node to check
* @return {Boolean} true if its the first in the its start line
*/
Expand All @@ -165,8 +166,9 @@ module.exports = {
} while (token.type === 'JSXText' && /^\s*$/.test(token.value));
var startLine = node.loc.start.line;
var endLine = token ? token.loc.end.line : -1;
var whitespaceOnly = token ? /\n\s*$/.test(token.value) : false;

return startLine !== endLine;
return startLine !== endLine || whitespaceOnly;
}

/**
Expand Down Expand Up @@ -218,41 +220,47 @@ module.exports = {
}
}

function getOpeningElementIndent(node) {
var prevToken = sourceCode.getTokenBefore(node);
// Use the parent in a list or an array
if (prevToken.type === 'JSXText' || prevToken.type === 'Punctuator' && prevToken.value === ',') {
prevToken = sourceCode.getNodeByRangeIndex(prevToken.start);
prevToken = prevToken.type === 'Literal' ? prevToken.parent : prevToken;
// Use the first non-punctuator token in a conditional expression
} else if (prevToken.type === 'Punctuator' && prevToken.value === ':') {
do {
prevToken = sourceCode.getTokenBefore(prevToken);
} while (prevToken.type === 'Punctuator');
prevToken = sourceCode.getNodeByRangeIndex(prevToken.start);
while (prevToken.parent && prevToken.parent.type !== 'ConditionalExpression') {
prevToken = prevToken.parent;
}
}
prevToken = prevToken.type === 'JSXExpressionContainer' ? prevToken.expression : prevToken;

var parentElementIndent = getNodeIndent(prevToken);
var indent = (
prevToken.loc.start.line === node.loc.start.line ||
isRightInLogicalExp(node) ||
isAlternateInConditionalExp(node)
) ? 0 : indentSize;
return parentElementIndent + indent;
}

return {
JSXOpeningElement: function(node) {
var prevToken = sourceCode.getTokenBefore(node);
if (!prevToken) {
return;
}
// Use the parent in a list or an array
if (prevToken.type === 'JSXText' || prevToken.type === 'Punctuator' && prevToken.value === ',') {
prevToken = sourceCode.getNodeByRangeIndex(prevToken.start);
prevToken = prevToken.type === 'Literal' ? prevToken.parent : prevToken;
// Use the first non-punctuator token in a conditional expression
} else if (prevToken.type === 'Punctuator' && prevToken.value === ':') {
do {
prevToken = sourceCode.getTokenBefore(prevToken);
} while (prevToken.type === 'Punctuator');
prevToken = sourceCode.getNodeByRangeIndex(prevToken.start);
while (prevToken.parent && prevToken.parent.type !== 'ConditionalExpression') {
prevToken = prevToken.parent;
}
}
prevToken = prevToken.type === 'JSXExpressionContainer' ? prevToken.expression : prevToken;

var parentElementIndent = getNodeIndent(prevToken);
var indent = (
prevToken.loc.start.line === node.loc.start.line ||
isRightInLogicalExp(node) ||
isAlternateInConditionalExp(node)
) ? 0 : indentSize;
checkNodesIndent(node, parentElementIndent + indent);
var indent = getOpeningElementIndent(node, prevToken);
checkNodesIndent(node, indent);
},
JSXClosingElement: function(node) {
if (!node.parent) {
return;
}
var peerElementIndent = getNodeIndent(node.parent.openingElement);
var peerElementIndent = getOpeningElementIndent(node.parent.openingElement);
checkNodesIndent(node, peerElementIndent);
},
JSXExpressionContainer: function(node) {
Expand Down
31 changes: 30 additions & 1 deletion tests/lib/rules/jsx-indent.js
Expand Up @@ -459,6 +459,34 @@ ruleTester.run('jsx-indent', rule, {
options: ['tab'],
parserOptions: parserOptions,
errors: [{message: 'Expected indentation of 1 tab character but found 0.'}]
}, {
code: [
'function MyComponent(props) {',
'\treturn (',
' <div',
'\t\t\tclassName="foo-bar"',
'\t\t\tid="thing"',
' >',
' Hello world!',
' </div>',
'\t)',
'}'
].join('\n'),
output: [
'function MyComponent(props) {',
'\treturn (',
'\t\t<div',
'\t\t\tclassName="foo-bar"',
'\t\t\tid="thing"',
'\t\t>',
'\t\t\tHello world!',
'\t\t</div>',
'\t)',
'}'
].join('\n'),
options: ['tab'],
parserOptions: parserOptions,
errors: [{message: 'Expected indentation of 2 tab characters but found 0.'}]
}, {
code: [
'function App() {',
Expand Down Expand Up @@ -730,7 +758,8 @@ ruleTester.run('jsx-indent', rule, {
].join('\n'),
parserOptions: parserOptions,
errors: [
{message: 'Expected indentation of 4 space characters but found 0.'}
{message: 'Expected indentation of 2 tab characters but found 0.'},
{message: 'Expected indentation of 2 tab characters but found 0.'}
]
}, {
// Multiline ternary
Expand Down

0 comments on commit 2141cb5

Please sign in to comment.