diff --git a/docs/rules/jsx-max-props-per-line.md b/docs/rules/jsx-max-props-per-line.md
index ff50d50a4c..490b50e0f1 100644
--- a/docs/rules/jsx-max-props-per-line.md
+++ b/docs/rules/jsx-max-props-per-line.md
@@ -4,12 +4,16 @@ Limiting the maximum of props on a single line can improve readability.
## Rule Details
-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`.
+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`.
The following patterns are considered warnings:
```jsx
;
+
+;
```
The following patterns are not considered warnings:
@@ -31,7 +35,7 @@ The following patterns are not considered warnings:
```js
...
-"jsx-max-props-per-line": [, { "maximum": }]
+"jsx-max-props-per-line": [, { "maximum": , "when": }]
...
```
@@ -42,20 +46,42 @@ Maximum number of props allowed on a single line. Default to `1`.
The following patterns are considered warnings:
```jsx
-// [1, {maximum: 2}]
+// [1, { "maximum": 2 }]
;
```
The following patterns are not considered warnings:
```jsx
-// [1, {maximum: 2}]
+// [1, { "maximum": 2 }]
;
```
+### `when`
+
+Possible values:
+- `always` (default) - Always check for max props per line.
+- `multiline` - Only check for max props per line when jsx tag spans multiple lines.
+
+The following patterns are considered warnings:
+```jsx
+// [1, { "when": "always" }]
+
+```
+
+The following patterns are not considered warnings:
+```jsx
+// [1, { "when": "multiline" }]
+
+
+```
+
## When not to use
If you are not using JSX then you can disable this rule.
diff --git a/lib/rules/jsx-max-props-per-line.js b/lib/rules/jsx-max-props-per-line.js
index 0838edc68c..1157784ca0 100644
--- a/lib/rules/jsx-max-props-per-line.js
+++ b/lib/rules/jsx-max-props-per-line.js
@@ -5,8 +5,6 @@
'use strict';
-var has = require('has');
-
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
@@ -25,6 +23,10 @@ module.exports = {
maximum: {
type: 'integer',
minimum: 1
+ },
+ when: {
+ type: 'string',
+ enum: ['always', 'multiline']
}
}
}]
@@ -35,6 +37,7 @@ module.exports = {
var sourceCode = context.getSourceCode();
var configuration = context.options[0] || {};
var maximum = configuration.maximum || 1;
+ var when = configuration.when || 'always';
function getPropName(propNode) {
if (propNode.type === 'JSXSpreadAttribute') {
@@ -45,30 +48,35 @@ module.exports = {
return {
JSXOpeningElement: function (node) {
- var props = {};
+ if (!node.attributes.length) {
+ return;
+ }
+
+ if (when === 'multiline' && node.loc.start.line === node.loc.end.line) {
+ return;
+ }
- node.attributes.forEach(function(decl) {
- var line = decl.loc.start.line;
- if (props[line]) {
- props[line].push(decl);
+ var firstProp = node.attributes[0];
+ var linePartitionedProps = [[firstProp]];
+
+ node.attributes.reduce(function(last, decl) {
+ if (last.loc.end.line === decl.loc.start.line) {
+ linePartitionedProps[linePartitionedProps.length - 1].push(decl);
} else {
- props[line] = [decl];
+ linePartitionedProps.push([decl]);
}
+ return decl;
});
- for (var line in props) {
- if (!has(props, line)) {
- continue;
- }
- if (props[line].length > maximum) {
- var name = getPropName(props[line][maximum]);
+ linePartitionedProps.forEach(function(propsInLine) {
+ if (propsInLine.length > maximum) {
+ var name = getPropName(propsInLine[maximum]);
context.report({
- node: props[line][maximum],
+ node: propsInLine[maximum],
message: 'Prop `' + name + '` must be placed on a new line'
});
- break;
}
- }
+ });
}
};
}
diff --git a/tests/lib/rules/jsx-max-props-per-line.js b/tests/lib/rules/jsx-max-props-per-line.js
index 982936e0be..55ce2d2e79 100644
--- a/tests/lib/rules/jsx-max-props-per-line.js
+++ b/tests/lib/rules/jsx-max-props-per-line.js
@@ -25,12 +25,27 @@ var parserOptions = {
var ruleTester = new RuleTester();
ruleTester.run('jsx-max-props-per-line', rule, {
valid: [{
+ code: '',
+ parserOptions: parserOptions
+ }, {
code: '',
parserOptions: parserOptions
}, {
code: '',
options: [{maximum: 2}],
parserOptions: parserOptions
+ }, {
+ code: '',
+ options: [{when: 'multiline'}],
+ parserOptions: parserOptions
+ }, {
+ code: '',
+ options: [{when: 'multiline'}],
+ parserOptions: parserOptions
+ }, {
+ code: '',
+ options: [{maximum: 2, when: 'multiline'}],
+ parserOptions: parserOptions
}, {
code: '',
options: [{maximum: 2}],
@@ -89,5 +104,64 @@ ruleTester.run('jsx-max-props-per-line', rule, {
].join('\n'),
errors: [{message: 'Prop `this.props` must be placed on a new line'}],
parserOptions: parserOptions
+ }, {
+ code: [
+ ''
+ ].join('\n'),
+ errors: [{message: 'Prop `bar` must be placed on a new line'}],
+ parserOptions: parserOptions
+ }, {
+ code: [
+ ''
+ ].join('\n'),
+ errors: [{message: 'Prop `bar` must be placed on a new line'}],
+ parserOptions: parserOptions
+ }, {
+ code: [
+ ''
+ ].join('\n'),
+ options: [{maximum: 2}],
+ errors: [{message: 'Prop `baz` must be placed on a new line'}],
+ parserOptions: parserOptions
+ }, {
+ code: [
+ ''
+ ].join('\n'),
+ errors: [{message: 'Prop `rest` must be placed on a new line'}],
+ parserOptions: parserOptions
+ }, {
+ code: [
+ ''
+ ].join('\n'),
+ errors: [{message: 'Prop `bar` must be placed on a new line'}],
+ parserOptions: parserOptions
+ }, {
+ code: [
+ ''
+ ].join('\n'),
+ errors: [{message: 'Prop `rest` must be placed on a new line'}],
+ parserOptions: parserOptions
+ }, {
+ code: [
+ ''
+ ].join('\n'),
+ options: [{maximum: 2}],
+ errors: [{message: 'Prop `baz` must be placed on a new line'}],
+ parserOptions: parserOptions
}]
});