Skip to content

Commit 491f67f

Browse files
committed
Add jsx-max-props-per-line rule (fixes #13)
1 parent f8a8bd7 commit 491f67f

File tree

5 files changed

+213
-2
lines changed

5 files changed

+213
-2
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ Finally, enable all of the rules that you would like to use.
4444
"rules": {
4545
"react/display-name": 1,
4646
"react/jsx-boolean-value": 1,
47+
"react/jsx-curly-spacing": 1,
48+
"react/jsx-max-props-per-line": 1,
4749
"react/jsx-no-duplicate-props": 1,
4850
"react/jsx-no-undef": 1,
4951
"react/jsx-quotes": 1,
@@ -71,6 +73,7 @@ Finally, enable all of the rules that you would like to use.
7173
* [display-name](docs/rules/display-name.md): Prevent missing displayName in a React component definition
7274
* [jsx-boolean-value](docs/rules/jsx-boolean-value.md): Enforce boolean attributes notation in JSX
7375
* [jsx-curly-spacing](docs/rules/jsx-curly-spacing.md): Enforce or disallow spaces inside of curly braces in JSX attributes
76+
* [jsx-max-props-per-line](docs/rules/jsx-max-props-per-line.md): Limit maximum of props on a single line in JSX
7477
* [jsx-no-duplicate-props](docs/rules/jsx-no-duplicate-props.md): Prevent duplicate props in JSX
7578
* [jsx-no-undef](docs/rules/jsx-no-undef.md): Disallow undeclared variables in JSX
7679
* [jsx-quotes](docs/rules/jsx-quotes.md): Enforce quote style for JSX attributes

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Limit maximum of props on a single line in JSX (jsx-max-props-per-line)
2+
3+
Limiting the maximum of props on a single line can improve readability.
4+
5+
## Rule Details
6+
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 count as one prop. This rule is off by default and when on the default maximum of props on one line is `1`.
8+
9+
The following patterns are considered warnings:
10+
11+
```jsx
12+
<Hello lastName="Smith" firstName="John" />;
13+
```
14+
15+
The following patterns are not considered warnings:
16+
17+
```jsx
18+
<Hello
19+
firstName="John"
20+
lastName="Smith"
21+
/>;
22+
23+
<Hello
24+
{...this.props}
25+
firstName="John"
26+
lastName="Smith"
27+
/>;
28+
```
29+
30+
## Rule Options
31+
32+
```js
33+
...
34+
"jsx-max-props-per-line": [<enabled>, { "maximum": <number> }]
35+
...
36+
```
37+
38+
### `maximum`
39+
40+
Maximum number of props allowed on a single line. Default to `1`.
41+
42+
The following patterns are considered warnings:
43+
44+
```jsx
45+
// [1, {maximum: 2}]
46+
<Hello firstName="John" lastName="Smith" tel={5555555} />;
47+
```
48+
49+
The following patterns are not considered warnings:
50+
51+
```jsx
52+
// [1, {maximum: 2}]
53+
<Hello
54+
firstName="John" lastName="Smith"
55+
tel={5555555}
56+
/>;
57+
```
58+
59+
## When not to use
60+
61+
If you are not using JSX then you can disable this rule.

index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ module.exports = {
2222
'jsx-boolean-value': require('./lib/rules/jsx-boolean-value'),
2323
'sort-comp': require('./lib/rules/sort-comp'),
2424
'require-extension': require('./lib/rules/require-extension'),
25-
'jsx-no-duplicate-props': require('./lib/rules/jsx-no-duplicate-props')
25+
'jsx-no-duplicate-props': require('./lib/rules/jsx-no-duplicate-props'),
26+
'jsx-max-props-per-line': require('./lib/rules/jsx-max-props-per-line')
2627
},
2728
rulesConfig: {
2829
'jsx-uses-react': 0,
@@ -45,6 +46,7 @@ module.exports = {
4546
'jsx-boolean-value': 0,
4647
'sort-comp': 0,
4748
'require-extension': 0,
48-
'jsx-no-duplicate-props': 0
49+
'jsx-no-duplicate-props': 0,
50+
'jsx-max-props-per-line': 0
4951
}
5052
};

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

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* @fileoverview Limit maximum of props on a single line in JSX
3+
* @author Yannick Croissant
4+
*/
5+
6+
'use strict';
7+
8+
// ------------------------------------------------------------------------------
9+
// Rule Definition
10+
// ------------------------------------------------------------------------------
11+
12+
module.exports = function (context) {
13+
14+
var configuration = context.options[0] || {};
15+
var maximum = configuration.maximum || 1;
16+
17+
function getPropName(propNode) {
18+
if (propNode.type === 'JSXSpreadAttribute') {
19+
return context.getSource(propNode.argument);
20+
}
21+
return propNode.name.name;
22+
}
23+
24+
return {
25+
JSXOpeningElement: function (node) {
26+
var props = {};
27+
28+
node.attributes.forEach(function(decl) {
29+
var line = decl.loc.start.line;
30+
if (props[line]) {
31+
props[line].push(decl);
32+
} else {
33+
props[line] = [decl];
34+
}
35+
});
36+
37+
for (var line in props) {
38+
if (!props.hasOwnProperty(line)) {
39+
continue;
40+
}
41+
if (props[line].length > maximum) {
42+
var name = getPropName(props[line][maximum]);
43+
context.report(props[line][maximum], 'Prop `' + name + '` must be placed on a new line');
44+
break;
45+
}
46+
}
47+
}
48+
};
49+
};
50+
51+
module.exports.schema = [{
52+
type: 'object',
53+
properties: {
54+
maximum: {
55+
type: 'integer',
56+
minimum: 1
57+
}
58+
}
59+
}];
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
* @fileoverview Limit maximum of props on a single line in JSX
3+
* @author Yannick Croissant
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
var eslint = require('eslint').linter;
12+
var ESLintTester = require('eslint').ESLintTester;
13+
14+
// ------------------------------------------------------------------------------
15+
// Tests
16+
// ------------------------------------------------------------------------------
17+
18+
var eslintTester = new ESLintTester(eslint);
19+
eslintTester.addRuleTest('lib/rules/jsx-max-props-per-line', {
20+
valid: [{
21+
code: '<App foo />',
22+
ecmaFeatures: {jsx: true}
23+
}, {
24+
code: '<App foo bar />',
25+
options: [{maximum: 2}],
26+
ecmaFeatures: {jsx: true}
27+
}, {
28+
code: '<App {...this.props} bar />',
29+
options: [{maximum: 2}],
30+
ecmaFeatures: {jsx: true}
31+
}, {
32+
code: [
33+
'<App',
34+
' foo',
35+
' bar',
36+
'/>'
37+
].join('\n'),
38+
ecmaFeatures: {jsx: true}
39+
}, {
40+
code: [
41+
'<App',
42+
' foo bar',
43+
' baz',
44+
'/>'
45+
].join('\n'),
46+
options: [{maximum: 2}],
47+
ecmaFeatures: {jsx: true}
48+
}],
49+
50+
invalid: [{
51+
code: '<App foo bar baz />;',
52+
errors: [{message: 'Prop `bar` must be placed on a new line'}],
53+
ecmaFeatures: {jsx: true}
54+
}, {
55+
code: '<App foo bar baz />;',
56+
options: [{maximum: 2}],
57+
errors: [{message: 'Prop `baz` must be placed on a new line'}],
58+
ecmaFeatures: {jsx: true}
59+
}, {
60+
code: '<App {...this.props} bar />;',
61+
errors: [{message: 'Prop `bar` must be placed on a new line'}],
62+
ecmaFeatures: {jsx: true}
63+
}, {
64+
code: '<App bar {...this.props} />;',
65+
errors: [{message: 'Prop `this.props` must be placed on a new line'}],
66+
ecmaFeatures: {jsx: true}
67+
}, {
68+
code: [
69+
'<App',
70+
' foo bar',
71+
' baz',
72+
'/>'
73+
].join('\n'),
74+
errors: [{message: 'Prop `bar` must be placed on a new line'}],
75+
ecmaFeatures: {jsx: true}
76+
}, {
77+
code: [
78+
'<App',
79+
' foo {...this.props}',
80+
' baz',
81+
'/>'
82+
].join('\n'),
83+
errors: [{message: 'Prop `this.props` must be placed on a new line'}],
84+
ecmaFeatures: {jsx: true}
85+
}]
86+
});

0 commit comments

Comments
 (0)