Skip to content

Commit 995b258

Browse files
authored
Merge pull request jsx-eslint#1462 from DianaSuvorova/i216
[destructuring-assignment] : new rule
2 parents abfb526 + e80d3b6 commit 995b258

File tree

5 files changed

+470
-0
lines changed

5 files changed

+470
-0
lines changed

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#
8484

8585
* [react/boolean-prop-naming](docs/rules/boolean-prop-naming.md): Enforces consistent naming for boolean props
8686
* [react/default-props-match-prop-types](docs/rules/default-props-match-prop-types.md): Prevent extraneous defaultProps on components
87+
* [react/destructuring-assignment](docs/rules/destructuring-assignment.md): Rule enforces consistent usage of destructuring assignment in component
8788
* [react/display-name](docs/rules/display-name.md): Prevent missing `displayName` in a React component definition
8889
* [react/forbid-component-props](docs/rules/forbid-component-props.md): Forbid certain props on Components
8990
* [react/forbid-elements](docs/rules/forbid-elements.md): Forbid certain elements

Diff for: docs/rules/destructuring-assignment.md

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Enforce consistent usage of destructuring assignment of props, state, and context (react/destructuring-assignment)
2+
3+
Rule can be set to either of `always` or `never`;
4+
```js
5+
"react/destructuring-assignment": [<enabled>, 'always']
6+
```
7+
8+
## Rule Details
9+
10+
By default rule is set to `always` enforce destructuring assignment. The following patterns are considered warnings:
11+
12+
```js
13+
const MyComponent = (props) => {
14+
return (<div id={props.id} />)
15+
};
16+
```
17+
18+
```js
19+
const Foo = class extends React.PureComponent {
20+
render() {
21+
return <div>{this.context.foo}</div>;
22+
}
23+
};
24+
```
25+
26+
Below pattern is correct:
27+
28+
```js
29+
const MyComponent = ({id}) => {
30+
return (<div id={id} />)
31+
};
32+
```
33+
34+
```js
35+
const MyComponent = (props, context) => {
36+
const { id } = props;
37+
return (<div id={id} />)
38+
};
39+
```
40+
41+
```js
42+
const Foo = class extends React.PureComponent {
43+
render() {
44+
const { title } = this.context;
45+
return <div>{title}</div>;
46+
}
47+
};
48+
```
49+
50+
If rule is set to `never`, the following patterns are considered warning:
51+
52+
```js
53+
const MyComponent = ({id}) => {
54+
return (<div id={id} />)
55+
};
56+
```
57+
58+
```js
59+
const MyComponent = (props) => {
60+
const { id } = props;
61+
return (<div id={id} />)
62+
};
63+
```
64+
65+
```js
66+
const Foo = class extends React.PureComponent {
67+
render() {
68+
const { title } = this.state;
69+
return <div>{title}</div>;
70+
}
71+
};
72+
```
73+
74+
and below pattern is correct:
75+
76+
```js
77+
const MyComponent = (props) => {
78+
return (<div id={props.id} />)
79+
};
80+
```
81+
82+
```js
83+
const Foo = class extends React.PureComponent {
84+
render() {
85+
return <div>{this.state.title}</div>;
86+
}
87+
};

Diff for: index.js

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const has = require('has');
55
const allRules = {
66
'boolean-prop-naming': require('./lib/rules/boolean-prop-naming'),
77
'default-props-match-prop-types': require('./lib/rules/default-props-match-prop-types'),
8+
'destructuring-assignment': require('./lib/rules/destructuring-assignment'),
89
'display-name': require('./lib/rules/display-name'),
910
'forbid-component-props': require('./lib/rules/forbid-component-props'),
1011
'forbid-elements': require('./lib/rules/forbid-elements'),

Diff for: lib/rules/destructuring-assignment.js

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/**
2+
* @fileoverview Enforce consistent usage of destructuring assignment of props, state, and context.
3+
**/
4+
'use strict';
5+
6+
const Components = require('../util/Components');
7+
8+
const DEFAULT_OPTION = 'always';
9+
10+
module.exports = {
11+
meta: {
12+
docs: {
13+
description: 'Enforce consistent usage of destructuring assignment of props, state, and context',
14+
category: 'Stylistic Issues',
15+
recommended: false
16+
},
17+
schema: [{
18+
type: 'string',
19+
enum: [
20+
'always',
21+
'never'
22+
]
23+
}]
24+
},
25+
26+
create: Components.detect((context, components, utils) => {
27+
const configuration = context.options[0] || DEFAULT_OPTION;
28+
29+
30+
/**
31+
* Checks if a prop is being assigned a value props.bar = 'bar'
32+
* @param {ASTNode} node The AST node being checked.
33+
* @returns {Boolean}
34+
*/
35+
36+
function isAssignmentToProp(node) {
37+
return (
38+
node.parent &&
39+
node.parent.type === 'AssignmentExpression' &&
40+
node.parent.left === node
41+
);
42+
}
43+
/**
44+
* @param {ASTNode} node We expect either an ArrowFunctionExpression,
45+
* FunctionDeclaration, or FunctionExpression
46+
*/
47+
function handleStatelessComponent(node) {
48+
const destructuringProps = node.params && node.params[0] && node.params[0].type === 'ObjectPattern';
49+
const destructuringContext = node.params && node.params[1] && node.params[1].type === 'ObjectPattern';
50+
51+
if (destructuringProps && components.get(node) && configuration === 'never') {
52+
context.report({
53+
node: node,
54+
message: 'Must never use destructuring props assignment in SFC argument'
55+
});
56+
} else if (destructuringContext && components.get(node) && configuration === 'never') {
57+
context.report({
58+
node: node,
59+
message: 'Must never use destructuring context assignment in SFC argument'
60+
});
61+
}
62+
}
63+
64+
function handleSFCUsage(node) {
65+
// props.aProp || context.aProp
66+
const isPropUsed = (node.object.name === 'props' || node.object.name === 'context') && !isAssignmentToProp(node);
67+
if (isPropUsed && configuration === 'always') {
68+
context.report({
69+
node: node,
70+
message: `Must use destructuring ${node.object.name} assignment`
71+
});
72+
}
73+
}
74+
75+
function handleClassUsage(node) {
76+
// this.props.Aprop || this.context.aProp || this.state.aState
77+
const isPropUsed = (node.object.type === 'MemberExpression' && node.object.object.type === 'ThisExpression' &&
78+
(node.object.property.name === 'props' || node.object.property.name === 'context' || node.object.property.name === 'state') &&
79+
node.object.type === 'MemberExpression'
80+
);
81+
82+
if (isPropUsed && configuration === 'always') {
83+
context.report({
84+
node: node,
85+
message: `Must use destructuring ${node.object.property.name} assignment`
86+
});
87+
}
88+
}
89+
90+
return {
91+
92+
FunctionDeclaration: handleStatelessComponent,
93+
94+
ArrowFunctionExpression: handleStatelessComponent,
95+
96+
FunctionExpression: handleStatelessComponent,
97+
98+
MemberExpression: function(node) {
99+
const SFCComponent = components.get(context.getScope(node).block);
100+
const classComponent = utils.getParentComponent(node);
101+
if (SFCComponent) {
102+
handleSFCUsage(node);
103+
}
104+
if (classComponent) {
105+
handleClassUsage(node, classComponent);
106+
}
107+
},
108+
109+
VariableDeclarator: function(node) {
110+
const classComponent = utils.getParentComponent(node);
111+
const SFCComponent = components.get(context.getScope(node).block);
112+
113+
const destructuring = (node.init && node.id && node.id.type === 'ObjectPattern');
114+
// let {foo} = props;
115+
const destructuringSFC = destructuring && (node.init.name === 'props' || node.init.name === 'context');
116+
// let {foo} = this.props;
117+
const destructuringClass = destructuring && node.init.object && node.init.object.type === 'ThisExpression' && (
118+
node.init.property.name === 'props' || node.init.property.name === 'context' || node.init.property.name === 'state'
119+
);
120+
121+
if (SFCComponent && destructuringSFC && configuration === 'never') {
122+
context.report({
123+
node: node,
124+
message: `Must never use destructuring ${node.init.name} assignment`
125+
});
126+
}
127+
128+
if (classComponent && destructuringClass && configuration === 'never') {
129+
context.report({
130+
node: node,
131+
message: `Must never use destructuring ${node.init.property.name} assignment`
132+
});
133+
}
134+
}
135+
};
136+
})
137+
};

0 commit comments

Comments
 (0)