Skip to content

Commit

Permalink
[feat] update rule to accept both adn line rules
Browse files Browse the repository at this point in the history
  • Loading branch information
Carlux committed Sep 20, 2021
1 parent 9799131 commit eaf1bd4
Show file tree
Hide file tree
Showing 3 changed files with 255 additions and 16 deletions.
10 changes: 10 additions & 0 deletions docs/rules/jsx-max-props-per-line.md
Expand Up @@ -39,6 +39,12 @@ Examples of **correct** code for this rule:
...
"react/jsx-max-props-per-line": [<enabled>, { "maximum": <number>, "when": <string> }]
...

// OR

...
"react/jsx-max-props-per-line": [<enabled>, { "maximum": { single <number> multi: <number> } }]
...
```

### `maximum`
Expand All @@ -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.
Expand Down
80 changes: 65 additions & 15 deletions lib/rules/jsx-max-props-per-line.js
Expand Up @@ -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') {
Expand All @@ -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) => {
Expand All @@ -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);
};
Expand All @@ -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;
}

Expand All @@ -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)
});
}
});
Expand Down
181 changes: 180 additions & 1 deletion tests/lib/rules/jsx-max-props-per-line.js
Expand Up @@ -60,7 +60,70 @@ ruleTester.run('jsx-max-props-per-line', rule, {
'/>'
].join('\n'),
options: [{maximum: 2}]
}],
}, {
code: [
'<App',
' foo bar',
' baz',
'/>'
].join('\n'),
options: [{maximum: {multi: 2}}]
}, {
code: [
'<App',
' bar',
' baz',
'/>'
].join('\n'),
options: [{maximum: {multi: 2, single: 1}}]
}, {
code: '<App foo baz bar />',
options: [{maximum: {multi: 2, single: 3}}]
}, {
code: '<App {...this.props} bar />',
options: [{maximum: {single: 2}}]
}, {
code: [
'<App',
' foo bar',
' baz bor',
'/>'
].join('\n'),
options: [{maximum: {multi: 2, single: 1}}]
}, {
code: '<App foo baz bar />',
options: [{maximum: {multi: 2}}]
}, {
code: [
'<App',
' foo bar',
' baz bor',
'/>'
].join('\n'),
options: [{maximum: {single: 1}}]
}, {
code: [
'<App foo bar',
' baz bor',
'/>'
].join('\n'),
options: [{maximum: {single: 2, multi: 2}}]
}, {
code: [
'<App foo bar',
' baz bor',
'/>'
].join('\n'),
options: [{maximum: 2}]
}, {
code: [
'<App foo',
' bar',
'/>'
].join('\n'),
options: [{maximum: 1, when: 'multiline'}]
}
],

invalid: [{
code: '<App foo bar baz />;',
Expand Down Expand Up @@ -266,5 +329,121 @@ ruleTester.run('jsx-max-props-per-line', rule, {
messageId: 'newLine',
data: {prop: 'baz'}
}]
},
{
code: '<App foo bar baz />',
output: [
'<App foo',
'bar',
'baz />'
].join('\n'),
options: [{maximum: {single: 1, multi: 1}}],
errors: [{
messageId: 'newLine',
data: {prop: 'bar'}
}]
}, {
code: [
'<App',
' foo bar baz',
'/>'
].join('\n'),
output: [
'<App',
' foo',
'bar',
'baz',
'/>'
].join('\n'),
options: [{maximum: {single: 1, multi: 1}}],
errors: [{
messageId: 'newLine',
data: {prop: 'bar'}
}]
}, {
code: [
'<App foo',
' bar baz',
'/>'
].join('\n'),
output: [
'<App foo',
' bar',
'baz',
'/>'
].join('\n'),
options: [{maximum: {single: 1, multi: 1}}],
errors: [{
messageId: 'newLine',
data: {prop: 'baz'}
}]
}, {
code: [
'<App foo bar',
' bar baz bor',
'/>'
].join('\n'),
output: [
'<App foo bar',
' bar baz',
'bor',
'/>'
].join('\n'),
options: [{maximum: {single: 1, multi: 2}}],
errors: [
{
messageId: 'newLine',
data: {prop: 'bor'}
}]
}, {
code: '<App foo bar baz bor />',
output: [
'<App foo bar baz',
'bor />'
].join('\n'),
options: [{maximum: {single: 3, multi: 2}}],
errors: [
{
messageId: 'newLine',
data: {prop: 'bor'}
}]
}, {
code: [
'<App',
' foo={{',
' }} bar baz bor',
'/>'
].join('\n'),
output: [
'<App',
' foo={{',
' }} bar',
'baz bor',
'/>'
].join('\n'),
options: [{maximum: {multi: 2}}],
errors: [{
messageId: 'newLine',
data: {prop: 'baz'}
}]
}, {
code: [
'<App boz fuz',
' foo={{',
' }} bar baz bor',
'/>'
].join('\n'),
output: [
'<App boz fuz',
' foo={{',
' }} bar',
'baz bor',
'/>'
].join('\n'),
options: [{maximum: {multi: 2, single: 1}}],
errors: [{
messageId: 'newLine',
data: {prop: 'baz'}
}]
}]
});

0 comments on commit eaf1bd4

Please sign in to comment.