diff --git a/lib/rules/jsx-first-prop-new-line.js b/lib/rules/jsx-first-prop-new-line.js index 4a37746572..16b623bcc2 100644 --- a/lib/rules/jsx-first-prop-new-line.js +++ b/lib/rules/jsx-first-prop-new-line.js @@ -15,14 +15,49 @@ module.exports = { category: 'Stylistic Issues', recommended: false }, + fixable: 'code', - schema: [{ - enum: ['always', 'never', 'multiline'] - }] + schema: [ + { + enum: ['always', 'never', 'multiline'] + }, + { + oneOf: [{ + enum: ['tab'] + }, { + type: 'integer' + }] + } + ] }, create: function (context) { var configuration = context.options[0]; + var indentType = 'space'; + var indentSize = 2; + var sourceCode = context.getSourceCode(); + + if (context.options.length > 1) { + if (context.options[1] === 'tab') { + indentSize = 1; + indentType = 'tab'; + } else if (typeof context.options[1] === 'number') { + indentSize = context.options[1]; + indentType = 'space'; + } + } + + function getNodeIndent(node) { + var src = sourceCode.getText(node, node.loc.start.column).split('\n')[0]; + var regExp; + if (indentType === 'space') { + regExp = new RegExp('^[ ]+'); + } else { + regExp = new RegExp('^[\t' + ']+'); + } + var indent = regExp.exec(src); + return indent ? indent[0].length : 0; + } function isMultilineJSX(jsxNode) { return jsxNode.loc.start.line < jsxNode.loc.end.line; @@ -35,7 +70,12 @@ module.exports = { if (decl.loc.start.line === node.loc.start.line) { context.report({ node: decl, - message: 'Property should be placed on a new line' + message: 'Property should be placed on a new line', + fix: function(fixer) { + var neededIndent = getNodeIndent(node) + indentSize; + var insert = '\n' + Array(neededIndent + 1).join(indentType === 'space' ? ' ' : '\t'); + return fixer.replaceTextRange([node.name.end, decl.start], insert); + } }); } }); @@ -44,7 +84,10 @@ module.exports = { if (node.loc.start.line < firstNode.loc.start.line) { context.report({ node: firstNode, - message: 'Property should be placed on the same line as the component declaration' + message: 'Property should be placed on the same line as the component declaration', + fix: function(fixer) { + return fixer.replaceTextRange([node.name.end, firstNode.start], ' '); + } }); return; } diff --git a/tests/lib/rules/jsx-first-prop-new-line.js b/tests/lib/rules/jsx-first-prop-new-line.js index 2fd2dab2c1..43edaf30c0 100644 --- a/tests/lib/rules/jsx-first-prop-new-line.js +++ b/tests/lib/rules/jsx-first-prop-new-line.js @@ -120,6 +120,10 @@ ruleTester.run('jsx-first-prop-new-line', rule, { invalid: [ { code: '', + output: [ + '' + ].join('\n'), options: ['always'], errors: [{message: 'Property should be placed on a new line'}], parser: parserOptions @@ -130,15 +134,58 @@ ruleTester.run('jsx-first-prop-new-line', rule, { ' propTwo="two"', '/>' ].join('\n'), + output: [ + '' + ].join('\n'), options: ['always'], errors: [{message: 'Property should be placed on a new line'}], parser: parserOptions }, + { + code: [ + ' ' + ].join('\n'), + output: [ + ' ' + ].join('\n'), + options: ['always', 4], + errors: [{message: 'Property should be placed on a new line'}], + parser: parserOptions + }, + { + code: [ + '\t' + ].join('\n'), + output: [ + '\t' + ].join('\n'), + options: ['always', 'tab'], + errors: [{message: 'Property should be placed on a new line'}], + parser: parserOptions + }, { code: [ '' + ].join('\n'), + output: [ + '' ].join('\n'), options: ['never'],