Skip to content

Commit

Permalink
feat[destructuring-props-argument] : new rule to enforce consistent u…
Browse files Browse the repository at this point in the history
…sage of destructuring assignment in component arguments
  • Loading branch information
DianaSuvorova committed Oct 3, 2017
1 parent 1f14fad commit d01afa8
Show file tree
Hide file tree
Showing 4 changed files with 195 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-props-argument](docs/rules/destructuring-props-argument.md): Rule enforces consistent usage of destructuring assignment in component arguments
* [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
45 changes: 45 additions & 0 deletions docs/rules/destructuring-props-argument.md
@@ -0,0 +1,45 @@
# Enforces consistent usage of destructuring props argument assignment(react/destructuring-props-argument)

Rule enforces consistent usage of destructuring assignment in component arguments.

Rule can be set to either of `always`, `never`, `ignore` for each type of the component.
Currently only SFC is implemented.

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

## Rule Details

By default rule is set to `always` enforce destructuring assignment. The following pattern is considered warning:

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

Below pattern is correct:

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


If rule option is set to `never`, 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} />)
};
```
89 changes: 89 additions & 0 deletions lib/rules/destructuring-props-argument.js
@@ -0,0 +1,89 @@
/**
* @fileoverview Rule to forbid or enforce destructuring props argument.
**/
'use strict';

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

const DEFAULT_OPTIONS = {
SFC: 'always'
};

module.exports = {
meta: {
docs: {
description: 'Forbid or enforce destructuring props argument',
category: 'Stylistic Issues',
recommended: false
},
schema: [{
definitions: {
value: {
enum: [
'always',
'never',
'ignore'
]
}
},
type: 'object',
properties: {
SFC: {$ref: '#/definitions/value'}
},
additionalProperties: false
}]
},

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


/**
* 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 destructuring = node.params && node.params[0] && node.params[0].type === 'ObjectPattern';
if (destructuring && components.get(node) && options.SFC === 'never') {
context.report({
node: node,
message: 'Should never use destructuring props assignment in SFC argument'
});
}
}

return {

FunctionDeclaration: handleStatelessComponent,

ArrowFunctionExpression: handleStatelessComponent,

FunctionExpression: handleStatelessComponent,

MemberExpression: function(node) {
const relatedComponent = components.get(context.getScope(node).block);
const isStatelessFunctionUsage = node.object.name === 'props' && !isAssignmentToProp(node);
if (relatedComponent && isStatelessFunctionUsage && options.SFC === 'always') {
context.report({
node: node,
message: 'Should use destructuring props assignment in SFC argument'
});
}
}
};
})
};
60 changes: 60 additions & 0 deletions tests/lib/rules/destructuring-props-argument.js
@@ -0,0 +1,60 @@

/**
* @fileoverview Rule to forbid or enforce destructuring props argument.
**/
'use strict';

const rule = require('../../../lib/rules/destructuring-props-argument');
const RuleTester = require('eslint').RuleTester;

require('babel-eslint');

const parserOptions = {
ecmaVersion: 6,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
};

const ruleTester = new RuleTester({parserOptions});
ruleTester.run('destructuring-props-argument', rule, {
valid: [{
code: `const MyComponent = ({ id, className }) => (
<div id={id} className={className} />
);`
}, {
code: `const MyComponent = (props) => (
<div id={id} props={props} />
);`
}, {
code: `const Foo = class extends React.PureComponent {
render() {
return <div>{this.props.foo}</div>;
}
};`
}, {
code: `class Foo extends React.Component {
doStuff() {}
render() {
return <div>{this.props.foo}</div>;
}
}`
}],
invalid: [{
code: `const MyComponent = (props) => {
return (<div id={props.id} />)
};`,
errors: [
{message: 'Should use destructuring props assignment in SFC argument'}
]
}, {
code: `const MyComponent = ({ id, className }) => (
<div id={id} className={className} />
);`,
options: [{SFC: 'never'}],
errors: [
{message: 'Should never use destructuring props assignment in SFC argument'}
]
}]
});

0 comments on commit d01afa8

Please sign in to comment.