Skip to content

Commit 0877bcd

Browse files
Paweł Nowakljharb
Paweł Nowak
authored andcommitted
[New] jsx-props-no-spreading: add explicitSpread option to allow explicit spread of props
Fixes #2439.
1 parent 03bdefc commit 0877bcd

File tree

3 files changed

+74
-7
lines changed

3 files changed

+74
-7
lines changed

docs/rules/jsx-props-no-spreading.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ const {one_prop, two_prop} = otherProps;
2727
```js
2828
...
2929
"react/jsx-props-no-spreading": [{
30-
"html": "ignore" / "enforce",
31-
"custom": "ignore" / "enforce",
30+
"html": "ignore" | "enforce",
31+
"custom": "ignore" | "enforce",
32+
"explicitSpread": "ignore" | "enforce",
3233
"exceptions": [<string>]
3334
}]
3435
...
@@ -65,6 +66,16 @@ The following patterns are still considered warnings:
6566
<img {...props} />
6667
```
6768

69+
### explicitSpread
70+
71+
`explicitSpread` set to `ignore` will ignore spread operators that are explicilty listing all object properties within that spread. Default is set to `enforce`.
72+
73+
The following pattern is **not** considered warning when `explicitSpread` is set to `ignore`:
74+
75+
```jsx
76+
<img {...{ prop1, prop2, prop3 }} />
77+
```
78+
6879
### exceptions
6980

7081
An "exception" will always flip the resulting html or custom setting for that component - ie, html set to `ignore`, with an exception of `div` will enforce on an `div`; custom set to `enforce` with an exception of `Foo` will ignore `Foo`.

lib/rules/jsx-props-no-spreading.js

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ const docsUrl = require('../util/docsUrl');
1212
// ------------------------------------------------------------------------------
1313

1414
const OPTIONS = {ignore: 'ignore', enforce: 'enforce'};
15-
const DEFAULTS = {html: OPTIONS.enforce, custom: OPTIONS.enforce, exceptions: []};
15+
const DEFAULTS = {
16+
html: OPTIONS.enforce,
17+
custom: OPTIONS.enforce,
18+
explicitSpread: OPTIONS.enforce,
19+
exceptions: []
20+
};
1621

1722
// ------------------------------------------------------------------------------
1823
// Rule Definition
@@ -70,21 +75,34 @@ module.exports = {
7075
const configuration = context.options[0] || {};
7176
const ignoreHtmlTags = (configuration.html || DEFAULTS.html) === OPTIONS.ignore;
7277
const ignoreCustomTags = (configuration.custom || DEFAULTS.custom) === OPTIONS.ignore;
78+
const ignoreExplicitSpread = (configuration.explicitSpread || DEFAULTS.explicitSpread) === OPTIONS.ignore;
7379
const exceptions = configuration.exceptions || DEFAULTS.exceptions;
7480
const isException = (tag, allExceptions) => allExceptions.indexOf(tag) !== -1;
81+
const isProperty = property => property.type === 'Property';
7582
return {
7683
JSXSpreadAttribute(node) {
7784
const tagName = node.parent.name.name;
7885
const isHTMLTag = tagName && tagName[0] !== tagName[0].toUpperCase();
7986
const isCustomTag = tagName && tagName[0] === tagName[0].toUpperCase();
80-
if (isHTMLTag &&
87+
if (
88+
isHTMLTag &&
8189
((ignoreHtmlTags && !isException(tagName, exceptions)) ||
82-
(!ignoreHtmlTags && isException(tagName, exceptions)))) {
90+
(!ignoreHtmlTags && isException(tagName, exceptions)))
91+
) {
8392
return;
8493
}
85-
if (isCustomTag &&
94+
if (
95+
isCustomTag &&
8696
((ignoreCustomTags && !isException(tagName, exceptions)) ||
87-
(!ignoreCustomTags && isException(tagName, exceptions)))) {
97+
(!ignoreCustomTags && isException(tagName, exceptions)))
98+
) {
99+
return;
100+
}
101+
if (
102+
ignoreExplicitSpread &&
103+
node.argument.type === 'ObjectExpression' &&
104+
node.argument.properties.every(isProperty)
105+
) {
88106
return;
89107
}
90108
context.report({

tests/lib/rules/jsx-props-no-spreading.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ ruleTester.run('jsx-props-no-spreading', rule, {
8282
'</App>'
8383
].join('\n'),
8484
options: [{html: 'ignore'}]
85+
}, {
86+
code: `
87+
<App>
88+
<Foo {...{ prop1, prop2, prop3 }} />
89+
</App>
90+
`,
91+
options: [{explicitSpread: 'ignore'}]
8592
}],
8693

8794
invalid: [{
@@ -154,5 +161,36 @@ ruleTester.run('jsx-props-no-spreading', rule, {
154161
].join('\n'),
155162
options: [{html: 'ignore'}],
156163
errors: [expectedError]
164+
}, {
165+
code: `
166+
<App>
167+
<Foo {...{ prop1, prop2, prop3 }} />
168+
</App>
169+
`,
170+
errors: [expectedError]
171+
}, {
172+
code: `
173+
<App>
174+
<Foo {...{ prop1, ...rest }} />
175+
</App>
176+
`,
177+
options: [{explicitSpread: 'ignore'}],
178+
errors: [expectedError]
179+
}, {
180+
code: `
181+
<App>
182+
<Foo {...{ ...props }} />
183+
</App>
184+
`,
185+
options: [{explicitSpread: 'ignore'}],
186+
errors: [expectedError]
187+
}, {
188+
code: `
189+
<App>
190+
<Foo {...props } />
191+
</App>
192+
`,
193+
options: [{explicitSpread: 'ignore'}],
194+
errors: [expectedError]
157195
}]
158196
});

0 commit comments

Comments
 (0)