diff --git a/lib/rules/jsx-indent.js b/lib/rules/jsx-indent.js index 6a75b6f64f..dd3d13c235 100644 --- a/lib/rules/jsx-indent.js +++ b/lib/rules/jsx-indent.js @@ -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 */ @@ -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; } /** @@ -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); + 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) { diff --git a/tests/lib/rules/jsx-indent.js b/tests/lib/rules/jsx-indent.js index a0072709c1..72734494f4 100644 --- a/tests/lib/rules/jsx-indent.js +++ b/tests/lib/rules/jsx-indent.js @@ -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 (', + ' ', + ' Hello world!', + ' ', + '\t)', + '}' + ].join('\n'), + output: [ + 'function MyComponent(props) {', + '\treturn (', + '\t\t', + '\t\t\tHello world!', + '\t\t', + '\t)', + '}' + ].join('\n'), + options: ['tab'], + parserOptions: parserOptions, + errors: [{message: 'Expected indentation of 2 tab characters but found 0.'}] }, { code: [ 'function App() {', @@ -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