Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jsx-max-props-per-line: autofix #949

Merged
merged 1 commit into from
May 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#
* [react/jsx-indent](docs/rules/jsx-indent.md): Validate JSX indentation (fixable)
* [react/jsx-indent-props](docs/rules/jsx-indent-props.md): Validate props indentation in JSX (fixable)
* [react/jsx-key](docs/rules/jsx-key.md): Validate JSX has key prop when in array or iterator
* [react/jsx-max-props-per-line](docs/rules/jsx-max-props-per-line.md): Limit maximum of props on a single line in JSX
* [react/jsx-max-props-per-line](docs/rules/jsx-max-props-per-line.md): Limit maximum of props on a single line in JSX (fixable)
* [react/jsx-no-bind](docs/rules/jsx-no-bind.md): Prevent usage of `.bind()` and arrow functions in JSX props
* [react/jsx-no-comment-textnodes](docs/rules/jsx-no-comment-textnodes.md): Prevent comments from being inserted as text nodes
* [react/jsx-no-duplicate-props](docs/rules/jsx-no-duplicate-props.md): Prevent duplicate props in JSX
Expand Down
2 changes: 2 additions & 0 deletions docs/rules/jsx-max-props-per-line.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Limiting the maximum of props on a single line can improve readability.

**Fixable:** This rule is automatically fixable using the `--fix` flag on the command line. However, fix does not include indentation. Please rerun lint to correct those errors.

## Rule Details

This rule checks all JSX elements and verifies that the number of props per line do not exceed the maximum allowed. Props are considered to be in a new line if there is a line break between the start of the prop and the end of the previous prop. A spread attribute counts as one prop. This rule is off by default and when on the default maximum of props on one line is `1`.
Expand Down
28 changes: 24 additions & 4 deletions lib/rules/jsx-max-props-per-line.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module.exports = {
category: 'Stylistic Issues',
recommended: false
},

fixable: 'code',
schema: [{
type: 'object',
properties: {
Expand Down Expand Up @@ -46,6 +46,25 @@ module.exports = {
return propNode.name.name;
}

function generateFixFunction(line, max) {
var output = [];
var front = line[0].start;
var back = line[line.length - 1].end;
for (var i = 0; i < line.length; i += max) {
var nodes = line.slice(i, i + max);
output.push(nodes.reduce(function(prev, curr) {
if (prev === '') {
return sourceCode.getText(curr);
}
return `${prev} ${sourceCode.getText(curr)}`;
}, ''));
}
var code = output.join('\n');
return function(fixer) {
return fixer.replaceTextRange([front, back], code);
};
}

return {
JSXOpeningElement: function (node) {
if (!node.attributes.length) {
Expand All @@ -59,7 +78,7 @@ module.exports = {
var firstProp = node.attributes[0];
var linePartitionedProps = [[firstProp]];

node.attributes.reduce(function(last, decl) {
node.attributes.reduce(function (last, decl) {
if (last.loc.end.line === decl.loc.start.line) {
linePartitionedProps[linePartitionedProps.length - 1].push(decl);
} else {
Expand All @@ -68,12 +87,13 @@ module.exports = {
return decl;
});

linePartitionedProps.forEach(function(propsInLine) {
linePartitionedProps.forEach(function (propsInLine) {
if (propsInLine.length > maximum) {
var name = getPropName(propsInLine[maximum]);
context.report({
node: propsInLine[maximum],
message: `Prop \`${name}\` must be placed on a new line`
message: `Prop \`${name}\` must be placed on a new line`,
fix: generateFixFunction(propsInLine, maximum)
});
}
});
Expand Down
106 changes: 95 additions & 11 deletions tests/lib/rules/jsx-max-props-per-line.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,67 +64,135 @@ ruleTester.run('jsx-max-props-per-line', rule, {

invalid: [{
code: '<App foo bar baz />;',
errors: [{message: 'Prop `bar` must be placed on a new line'}]
output: [
'<App foo',
'bar',
'baz />;'
].join('\n'),
errors: [{message: 'Prop `bar` must be placed on a new line'}],
parserOptions: parserOptions
}, {
code: '<App foo bar baz />;',
output: [
'<App foo bar',
'baz />;'
].join('\n'),
options: [{maximum: 2}],
errors: [{message: 'Prop `baz` must be placed on a new line'}]
}, {
code: '<App {...this.props} bar />;',
errors: [{message: 'Prop `bar` must be placed on a new line'}]
output: [
'<App {...this.props}',
'bar />;'
].join('\n'),
errors: [{message: 'Prop `bar` must be placed on a new line'}],
parserOptions: parserOptions
}, {
code: '<App bar {...this.props} />;',
errors: [{message: 'Prop `this.props` must be placed on a new line'}]
output: [
'<App bar',
'{...this.props} />;'
].join('\n'),
errors: [{message: 'Prop `this.props` must be placed on a new line'}],
parserOptions: parserOptions
}, {
code: [
'<App',
' foo bar',
' baz',
'/>'
].join('\n'),
errors: [{message: 'Prop `bar` must be placed on a new line'}]
output: [
'<App',
' foo',
'bar',
' baz',
'/>'
].join('\n'),
errors: [{message: 'Prop `bar` must be placed on a new line'}],
parserOptions: parserOptions
}, {
code: [
'<App',
' foo {...this.props}',
' baz',
'/>'
].join('\n'),
errors: [{message: 'Prop `this.props` must be placed on a new line'}]
output: [
'<App',
' foo',
'{...this.props}',
' baz',
'/>'
].join('\n'),
errors: [{message: 'Prop `this.props` must be placed on a new line'}],
parserOptions: parserOptions
}, {
code: [
'<App',
' foo={{',
' }} bar',
'/>'
].join('\n'),
errors: [{message: 'Prop `bar` must be placed on a new line'}]
output: [
'<App',
' foo={{',
' }}',
'bar',
'/>'
].join('\n'),
errors: [{message: 'Prop `bar` must be placed on a new line'}],
parserOptions: parserOptions
}, {
code: [
'<App foo={{',
'}} bar />'
].join('\n'),
errors: [{message: 'Prop `bar` must be placed on a new line'}]
output: [
'<App foo={{',
'}}',
'bar />'
].join('\n'),
errors: [{message: 'Prop `bar` must be placed on a new line'}],
parserOptions: parserOptions
}, {
code: [
'<App foo bar={{',
'}} baz />'
].join('\n'),
output: [
'<App foo bar={{',
'}}',
'baz />'
].join('\n'),
options: [{maximum: 2}],
errors: [{message: 'Prop `baz` must be placed on a new line'}]
}, {
code: [
'<App foo={{',
'}} {...rest} />'
].join('\n'),
errors: [{message: 'Prop `rest` must be placed on a new line'}]
output: [
'<App foo={{',
'}}',
'{...rest} />'
].join('\n'),
errors: [{message: 'Prop `rest` must be placed on a new line'}],
parserOptions: parserOptions
}, {
code: [
'<App {',
' ...this.props',
'} bar />'
].join('\n'),
errors: [{message: 'Prop `bar` must be placed on a new line'}]
output: [
'<App {',
' ...this.props',
'}',
'bar />'
].join('\n'),
errors: [{message: 'Prop `bar` must be placed on a new line'}],
parserOptions: parserOptions
}, {
code: [
'<App {',
Expand All @@ -133,12 +201,28 @@ ruleTester.run('jsx-max-props-per-line', rule, {
' ...rest',
'} />'
].join('\n'),
errors: [{message: 'Prop `rest` must be placed on a new line'}]
output: [
'<App {',
' ...this.props',
'}',
'{',
' ...rest',
'} />'
].join('\n'),
errors: [{message: 'Prop `rest` must be placed on a new line'}],
parserOptions: parserOptions
}, {
code: [
'<App',
' foo={{',
' }} bar baz',
' }} bar baz bor',
'/>'
].join('\n'),
output: [
'<App',
' foo={{',
' }} bar',
'baz bor',
'/>'
].join('\n'),
options: [{maximum: 2}],
Expand Down