Skip to content

Commit

Permalink
[destructuring-assignment] : new rule to enforce consistent usage of …
Browse files Browse the repository at this point in the history
…destructuring assignment
  • Loading branch information
DianaSuvorova committed Oct 10, 2017
1 parent 1f14fad commit a25003c
Show file tree
Hide file tree
Showing 5 changed files with 400 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -84,6 +84,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#

* [react/boolean-prop-naming](docs/rules/boolean-prop-naming.md): Enforces consistent naming for boolean props
* [react/default-props-match-prop-types](docs/rules/default-props-match-prop-types.md): Prevent extraneous defaultProps on components
* [react/destructuring-assignment](docs/rules/destructuring-assignment.md): Rule enforces consistent usage of destructuring assignment in component
* [react/display-name](docs/rules/display-name.md): Prevent missing `displayName` in a React component definition
* [react/forbid-component-props](docs/rules/forbid-component-props.md): Forbid certain props on Components
* [react/forbid-elements](docs/rules/forbid-elements.md): Forbid certain elements
Expand Down
58 changes: 58 additions & 0 deletions docs/rules/destructuring-assignment.md
@@ -0,0 +1,58 @@
# Enforce consostent usage of destructuring assignment of props, state and context (react/destructuring-assignment)

Rule can be set to either of `always`, `never`, `ignore` for SFC and to `always` or `ignore` for class components.

```js
"react/destructuring-assignment": [<enabled>, { "SFC": "always", "class": "always"}]
```

## Rule Details

By default rule is set to `always` enforce destructuring assignment. The following patterns are considered warnings:

```js
const MyComponent = (props) => {
return (<div id={props.id} />)
};
```

```js
const Foo = class extends React.PureComponent {
render() {
return <div>{this.context.foo}</div>;
}
};
```

Below pattern is correct:

```js
const MyComponent = ({id}) => {
return (<div id={id} />)
};
```

```js
const Foo = class extends React.PureComponent {
render() {
const { foo } = this.props;
return <div>{foo}</div>;
}
};
```

If rule option is set to `never` for SFC, the following pattern is considered warning:

```js
const MyComponent = ({id}) => {
return (<div id={id} />)
};
```

and below pattern is correct:

```js
const MyComponent = (props) => {
return (<div id={props.id} />)
};
```
1 change: 1 addition & 0 deletions index.js
Expand Up @@ -5,6 +5,7 @@ const has = require('has');
const allRules = {
'boolean-prop-naming': require('./lib/rules/boolean-prop-naming'),
'default-props-match-prop-types': require('./lib/rules/default-props-match-prop-types'),
'destructuring-assignment': require('./lib/rules/destructuring-assignment'),
'display-name': require('./lib/rules/display-name'),
'forbid-component-props': require('./lib/rules/forbid-component-props'),
'forbid-elements': require('./lib/rules/forbid-elements'),
Expand Down
133 changes: 133 additions & 0 deletions lib/rules/destructuring-assignment.js
@@ -0,0 +1,133 @@
/**
* @fileoverview Enforce consostent usage of destructuring assignment of props, state and context.
**/
'use strict';

const Components = require('../util/Components');

const DEFAULT_OPTIONS = {
SFC: 'always',
class: 'always'
};

module.exports = {
meta: {
docs: {
description: 'Enforce consostent usage of destructuring assignment of props, state and context',
category: 'Stylistic Issues',
recommended: false
},
schema: [{
definitions: {
valueSFC: {
enum: [
'always',
'never',
'ignore'
]
},
valueClass: {
enum: [
'always',
'ignore'
]
}
},
type: 'object',
properties: {
SFC: {$ref: '#/definitions/valueSFC'},
class: {$ref: '#/definitions/valueClass'}
},
additionalProperties: false
}]
},

create: Components.detect((context, components, utils) => {
const configuration = context.options[0] || {};
const options = {
SFC: configuration.SFC || DEFAULT_OPTIONS.SFC,
class: configuration.class || DEFAULT_OPTIONS.class
};


/**
* Checks if a prop is being assigned a value props.bar = 'bar'
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean}
*/

function isAssignmentToProp(node) {
return (
node.parent &&
node.parent.type === 'AssignmentExpression' &&
node.parent.left === node
);
}
/**
* @param {ASTNode} node We expect either an ArrowFunctionExpression,
* FunctionDeclaration, or FunctionExpression
*/
function handleStatelessComponent(node) {
const destructuringProps = node.params && node.params[0] && node.params[0].type === 'ObjectPattern';
const destructuringContext = node.params && node.params[1] && node.params[1].type === 'ObjectPattern';

if (destructuringProps && components.get(node) && options.SFC === 'never') {
context.report({
node: node,
message: 'Must never use destructuring props assignment in SFC argument'
});
} else if (destructuringContext && components.get(node) && options.SFC === 'never') {
context.report({
node: node,
message: 'Must never use destructuring context assignment in SFC argument'
});
}
}

function handleSFCUsage(node) {
// props.aProp || context.aProp
const isPropUsed = (node.object.name === 'props' || node.object.name === 'context') && !isAssignmentToProp(node);
if (isPropUsed && options.SFC === 'always') {
context.report({
node: node,
message: `Must use destructuring ${node.object.name} assignment`
});
}
}

function handleClassUsage(node) {
// this.props.Aprop || this.context.aProp || this.state.aState
const isPropUsed = (node.object.type === 'MemberExpression' && node.object.object.type === 'ThisExpression' &&
(node.object.property.name === 'props' || node.object.property.name === 'context' || node.object.property.name === 'state') &&
node.object.type === 'MemberExpression'
);

if (isPropUsed && options.class === 'always') {
context.report({
node: node,
message: `Must use destructuring ${node.object.property.name} assignment`
});
}
}

return {

FunctionDeclaration: handleStatelessComponent,

ArrowFunctionExpression: handleStatelessComponent,

FunctionExpression: handleStatelessComponent,

MemberExpression: function(node) {
const SFCComponent = components.get(context.getScope(node).block);
const classComponent = utils.getParentComponent(node);
if (SFCComponent) {
handleSFCUsage(node);
}
if (classComponent) {
handleClassUsage(node, classComponent);
}
}
};
})
};

0 comments on commit a25003c

Please sign in to comment.