diff --git a/docs/rules/jsx-max-props-per-line.md b/docs/rules/jsx-max-props-per-line.md index d40180c716..5bae2076cf 100644 --- a/docs/rules/jsx-max-props-per-line.md +++ b/docs/rules/jsx-max-props-per-line.md @@ -39,6 +39,12 @@ Examples of **correct** code for this rule: ... "react/jsx-max-props-per-line": [, { "maximum": , "when": }] ... + +// OR + +... +"react/jsx-max-props-per-line": [, { "maximum": { single multi: } }] +... ``` ### `maximum` @@ -62,8 +68,12 @@ Examples of **correct** code for this rule: />; ``` +Maximum can be specified as object `{ single: 1, multi: 1 }` to specify maximum allowed number of props for single line and multiple line tags. + ### `when` + _when only applied if `maximum` is specified as number._ + 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. diff --git a/lib/rules/jsx-max-props-per-line.js b/lib/rules/jsx-max-props-per-line.js index 208891e070..64691b27c2 100644 --- a/lib/rules/jsx-max-props-per-line.js +++ b/lib/rules/jsx-max-props-per-line.js @@ -26,24 +26,61 @@ module.exports = { }, schema: [{ - type: 'object', - properties: { - maximum: { - type: 'integer', - minimum: 1 + anyOf: [{ + type: 'object', + properties: { + maximum: { + oneOf: [{ + type: 'integer', + minimum: 1 + }, { + type: 'object', + properties: { + single: { + type: 'integer', + minimum: 1 + }, + multi: { + type: 'integer', + minimum: 1 + } + } + }] + } }, - when: { - type: 'string', - enum: ['always', 'multiline'] + additionalProperties: false + }, { + type: 'object', + properties: { + maximum: { + type: 'number', + minimum: 1 + }, + when: { + type: 'string', + enum: ['always', 'multiline'] + } } - } + }] }] }, create(context) { const configuration = context.options[0] || {}; const maximum = configuration.maximum || 1; - const when = configuration.when || 'always'; + let maximumSingle = null; + let maximumMulti = null; + + const isExtendedConfig = typeof maximum !== 'number'; + + if (isExtendedConfig) { + maximumSingle = maximum.single || Infinity; + maximumMulti = maximum.multi || Infinity; + } + + const when = isExtendedConfig + ? 'always' + : configuration.when || 'always'; function getPropName(propNode) { if (propNode.type === 'JSXSpreadAttribute') { @@ -57,6 +94,7 @@ module.exports = { const output = []; const front = line[0].range[0]; const back = line[line.length - 1].range[1]; + for (let i = 0; i < line.length; i += max) { const nodes = line.slice(i, i + max); output.push(nodes.reduce((prev, curr) => { @@ -66,7 +104,9 @@ module.exports = { return `${prev} ${sourceCode.getText(curr)}`; }, '')); } + const code = output.join('\n'); + return function fix(fixer) { return fixer.replaceTextRange([front, back], code); }; @@ -78,7 +118,9 @@ module.exports = { return; } - if (when === 'multiline' && node.loc.start.line === node.loc.end.line) { + const isSingleLineTag = node.loc.start.line === node.loc.end.line; + + if (when === 'multiline' && isSingleLineTag) { return; } @@ -94,16 +136,24 @@ module.exports = { return decl; }); + let maxPropsCountPerLine = maximum; + linePartitionedProps.forEach((propsInLine) => { - if (propsInLine.length > maximum) { - const name = getPropName(propsInLine[maximum]); + if (isExtendedConfig) { + maxPropsCountPerLine = isSingleLineTag && propsInLine[0].loc.start.line === node.loc.start.line + ? maximumSingle + : maximumMulti; + } + + if (propsInLine.length > maxPropsCountPerLine) { + const name = getPropName(propsInLine[maxPropsCountPerLine]); context.report({ - node: propsInLine[maximum], + node: propsInLine[maxPropsCountPerLine], messageId: 'newLine', data: { prop: name }, - fix: generateFixFunction(propsInLine, maximum) + fix: generateFixFunction(propsInLine, maxPropsCountPerLine) }); } }); diff --git a/tests/lib/rules/jsx-max-props-per-line.js b/tests/lib/rules/jsx-max-props-per-line.js index 86e8d9203c..503b0e132a 100644 --- a/tests/lib/rules/jsx-max-props-per-line.js +++ b/tests/lib/rules/jsx-max-props-per-line.js @@ -60,7 +60,70 @@ ruleTester.run('jsx-max-props-per-line', rule, { '/>' ].join('\n'), options: [{maximum: 2}] - }], + }, { + code: [ + '' + ].join('\n'), + options: [{maximum: {multi: 2}}] + }, { + code: [ + '' + ].join('\n'), + options: [{maximum: {multi: 2, single: 1}}] + }, { + code: '', + options: [{maximum: {multi: 2, single: 3}}] + }, { + code: '', + options: [{maximum: {single: 2}}] + }, { + code: [ + '' + ].join('\n'), + options: [{maximum: {multi: 2, single: 1}}] + }, { + code: '', + options: [{maximum: {multi: 2}}] + }, { + code: [ + '' + ].join('\n'), + options: [{maximum: {single: 1}}] + }, { + code: [ + '' + ].join('\n'), + options: [{maximum: {single: 2, multi: 2}}] + }, { + code: [ + '' + ].join('\n'), + options: [{maximum: 2}] + }, { + code: [ + '' + ].join('\n'), + options: [{maximum: 1, when: 'multiline'}] + } + ], invalid: [{ code: ';', @@ -266,5 +329,121 @@ ruleTester.run('jsx-max-props-per-line', rule, { messageId: 'newLine', data: {prop: 'baz'} }] + }, + { + code: '', + output: [ + '' + ].join('\n'), + options: [{maximum: {single: 1, multi: 1}}], + errors: [{ + messageId: 'newLine', + data: {prop: 'bar'} + }] + }, { + code: [ + '' + ].join('\n'), + output: [ + '' + ].join('\n'), + options: [{maximum: {single: 1, multi: 1}}], + errors: [{ + messageId: 'newLine', + data: {prop: 'bar'} + }] + }, { + code: [ + '' + ].join('\n'), + output: [ + '' + ].join('\n'), + options: [{maximum: {single: 1, multi: 1}}], + errors: [{ + messageId: 'newLine', + data: {prop: 'baz'} + }] + }, { + code: [ + '' + ].join('\n'), + output: [ + '' + ].join('\n'), + options: [{maximum: {single: 1, multi: 2}}], + errors: [ + { + messageId: 'newLine', + data: {prop: 'bor'} + }] + }, { + code: '', + output: [ + '' + ].join('\n'), + options: [{maximum: {single: 3, multi: 2}}], + errors: [ + { + messageId: 'newLine', + data: {prop: 'bor'} + }] + }, { + code: [ + '' + ].join('\n'), + output: [ + '' + ].join('\n'), + options: [{maximum: {multi: 2}}], + errors: [{ + messageId: 'newLine', + data: {prop: 'baz'} + }] + }, { + code: [ + '' + ].join('\n'), + output: [ + '' + ].join('\n'), + options: [{maximum: {multi: 2, single: 1}}], + errors: [{ + messageId: 'newLine', + data: {prop: 'baz'} + }] }] });