Skip to content

Commit 3b25a3b

Browse files
authored
Merge pull request #882 from kentor/jsx-max-props-per-line-updates
Jsx max props per line updates
2 parents 54abb8b + a84d5f5 commit 3b25a3b

File tree

3 files changed

+129
-21
lines changed

3 files changed

+129
-21
lines changed

docs/rules/jsx-max-props-per-line.md

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@ Limiting the maximum of props on a single line can improve readability.
44

55
## Rule Details
66

7-
This rule checks all JSX elements and verifies that the number of props per line do not exceed the maximum allowed. 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`.
7+
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`.
88

99
The following patterns are considered warnings:
1010

1111
```jsx
1212
<Hello lastName="Smith" firstName="John" />;
13+
14+
<Hello foo={{
15+
bar
16+
}} baz />;
1317
```
1418

1519
The following patterns are not considered warnings:
@@ -31,7 +35,7 @@ The following patterns are not considered warnings:
3135

3236
```js
3337
...
34-
"jsx-max-props-per-line": [<enabled>, { "maximum": <number> }]
38+
"jsx-max-props-per-line": [<enabled>, { "maximum": <number>, "when": <string> }]
3539
...
3640
```
3741

@@ -42,20 +46,42 @@ Maximum number of props allowed on a single line. Default to `1`.
4246
The following patterns are considered warnings:
4347

4448
```jsx
45-
// [1, {maximum: 2}]
49+
// [1, { "maximum": 2 }]
4650
<Hello firstName="John" lastName="Smith" tel={5555555} />;
4751
```
4852

4953
The following patterns are not considered warnings:
5054

5155
```jsx
52-
// [1, {maximum: 2}]
56+
// [1, { "maximum": 2 }]
5357
<Hello
5458
firstName="John" lastName="Smith"
5559
tel={5555555}
5660
/>;
5761
```
5862

63+
### `when`
64+
65+
Possible values:
66+
- `always` (default) - Always check for max props per line.
67+
- `multiline` - Only check for max props per line when jsx tag spans multiple lines.
68+
69+
The following patterns are considered warnings:
70+
```jsx
71+
// [1, { "when": "always" }]
72+
<Hello firstName="John" lastName="Smith" />
73+
```
74+
75+
The following patterns are not considered warnings:
76+
```jsx
77+
// [1, { "when": "multiline" }]
78+
<Hello firstName="John" lastName="Smith" />
79+
<Hello
80+
firstName="John"
81+
lastName="Smith"
82+
/>
83+
```
84+
5985
## When not to use
6086

6187
If you are not using JSX then you can disable this rule.

lib/rules/jsx-max-props-per-line.js

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55

66
'use strict';
77

8-
var has = require('has');
9-
108
// ------------------------------------------------------------------------------
119
// Rule Definition
1210
// ------------------------------------------------------------------------------
@@ -25,6 +23,10 @@ module.exports = {
2523
maximum: {
2624
type: 'integer',
2725
minimum: 1
26+
},
27+
when: {
28+
type: 'string',
29+
enum: ['always', 'multiline']
2830
}
2931
}
3032
}]
@@ -35,6 +37,7 @@ module.exports = {
3537
var sourceCode = context.getSourceCode();
3638
var configuration = context.options[0] || {};
3739
var maximum = configuration.maximum || 1;
40+
var when = configuration.when || 'always';
3841

3942
function getPropName(propNode) {
4043
if (propNode.type === 'JSXSpreadAttribute') {
@@ -45,30 +48,35 @@ module.exports = {
4548

4649
return {
4750
JSXOpeningElement: function (node) {
48-
var props = {};
51+
if (!node.attributes.length) {
52+
return;
53+
}
54+
55+
if (when === 'multiline' && node.loc.start.line === node.loc.end.line) {
56+
return;
57+
}
4958

50-
node.attributes.forEach(function(decl) {
51-
var line = decl.loc.start.line;
52-
if (props[line]) {
53-
props[line].push(decl);
59+
var firstProp = node.attributes[0];
60+
var linePartitionedProps = [[firstProp]];
61+
62+
node.attributes.reduce(function(last, decl) {
63+
if (last.loc.end.line === decl.loc.start.line) {
64+
linePartitionedProps[linePartitionedProps.length - 1].push(decl);
5465
} else {
55-
props[line] = [decl];
66+
linePartitionedProps.push([decl]);
5667
}
68+
return decl;
5769
});
5870

59-
for (var line in props) {
60-
if (!has(props, line)) {
61-
continue;
62-
}
63-
if (props[line].length > maximum) {
64-
var name = getPropName(props[line][maximum]);
71+
linePartitionedProps.forEach(function(propsInLine) {
72+
if (propsInLine.length > maximum) {
73+
var name = getPropName(propsInLine[maximum]);
6574
context.report({
66-
node: props[line][maximum],
75+
node: propsInLine[maximum],
6776
message: 'Prop `' + name + '` must be placed on a new line'
6877
});
69-
break;
7078
}
71-
}
79+
});
7280
}
7381
};
7482
}

tests/lib/rules/jsx-max-props-per-line.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,27 @@ var parserOptions = {
2525
var ruleTester = new RuleTester();
2626
ruleTester.run('jsx-max-props-per-line', rule, {
2727
valid: [{
28+
code: '<App />',
29+
parserOptions: parserOptions
30+
}, {
2831
code: '<App foo />',
2932
parserOptions: parserOptions
3033
}, {
3134
code: '<App foo bar />',
3235
options: [{maximum: 2}],
3336
parserOptions: parserOptions
37+
}, {
38+
code: '<App foo bar />',
39+
options: [{when: 'multiline'}],
40+
parserOptions: parserOptions
41+
}, {
42+
code: '<App foo {...this.props} />',
43+
options: [{when: 'multiline'}],
44+
parserOptions: parserOptions
45+
}, {
46+
code: '<App foo bar baz />',
47+
options: [{maximum: 2, when: 'multiline'}],
48+
parserOptions: parserOptions
3449
}, {
3550
code: '<App {...this.props} bar />',
3651
options: [{maximum: 2}],
@@ -89,5 +104,64 @@ ruleTester.run('jsx-max-props-per-line', rule, {
89104
].join('\n'),
90105
errors: [{message: 'Prop `this.props` must be placed on a new line'}],
91106
parserOptions: parserOptions
107+
}, {
108+
code: [
109+
'<App',
110+
' foo={{',
111+
' }} bar',
112+
'/>'
113+
].join('\n'),
114+
errors: [{message: 'Prop `bar` must be placed on a new line'}],
115+
parserOptions: parserOptions
116+
}, {
117+
code: [
118+
'<App foo={{',
119+
'}} bar />'
120+
].join('\n'),
121+
errors: [{message: 'Prop `bar` must be placed on a new line'}],
122+
parserOptions: parserOptions
123+
}, {
124+
code: [
125+
'<App foo bar={{',
126+
'}} baz />'
127+
].join('\n'),
128+
options: [{maximum: 2}],
129+
errors: [{message: 'Prop `baz` must be placed on a new line'}],
130+
parserOptions: parserOptions
131+
}, {
132+
code: [
133+
'<App foo={{',
134+
'}} {...rest} />'
135+
].join('\n'),
136+
errors: [{message: 'Prop `rest` must be placed on a new line'}],
137+
parserOptions: parserOptions
138+
}, {
139+
code: [
140+
'<App {',
141+
' ...this.props',
142+
'} bar />'
143+
].join('\n'),
144+
errors: [{message: 'Prop `bar` must be placed on a new line'}],
145+
parserOptions: parserOptions
146+
}, {
147+
code: [
148+
'<App {',
149+
' ...this.props',
150+
'} {',
151+
' ...rest',
152+
'} />'
153+
].join('\n'),
154+
errors: [{message: 'Prop `rest` must be placed on a new line'}],
155+
parserOptions: parserOptions
156+
}, {
157+
code: [
158+
'<App',
159+
' foo={{',
160+
' }} bar baz',
161+
'/>'
162+
].join('\n'),
163+
options: [{maximum: 2}],
164+
errors: [{message: 'Prop `baz` must be placed on a new line'}],
165+
parserOptions: parserOptions
92166
}]
93167
});

0 commit comments

Comments
 (0)