Skip to content

Commit

Permalink
Merge branch 'master' into 226-unused-prop-types
Browse files Browse the repository at this point in the history
# Conflicts:
#	index.js
  • Loading branch information
Evgueni Naverniouk committed Aug 24, 2016
2 parents d923dc6 + 7024678 commit b2c8cf5
Show file tree
Hide file tree
Showing 13 changed files with 514 additions and 48 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -106,6 +106,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#
* [react/self-closing-comp](docs/rules/self-closing-comp.md): Prevent extra closing tags for components without children (fixable)
* [react/sort-comp](docs/rules/sort-comp.md): Enforce component methods order
* [react/sort-prop-types](docs/rules/sort-prop-types.md): Enforce propTypes declarations alphabetical sorting
* [react/style-prop-object](docs/rules/style-prop-object.md): Enforce style prop value being an object

## JSX-specific rules

Expand Down
51 changes: 44 additions & 7 deletions docs/rules/prefer-stateless-function.md
Expand Up @@ -8,32 +8,33 @@ This rule will check your class based React components for

* methods/properties other than `displayName`, `propTypes`, `render` and useless constructor (same detection as ESLint [no-useless-constructor rule](http://eslint.org/docs/rules/no-useless-constructor))
* instance property other than `this.props` and `this.context`
* extension of `React.PureComponent` ()
* presence of `ref` attribute in JSX
* `render` method that return anything but JSX: `undefined`, `null`, etc. (only in React <15.0.0, see [shared settings](https://github.com/yannickcr/eslint-plugin-react/blob/master/README.md#configuration) for React version configuration)

If none of these 4 elements are found, the rule will warn you to write this component as a pure function.
If none of these elements are found, the rule will warn you to write this component as a pure function.

The following pattern is considered warnings:
The following pattern is considered a warning:

```js
```jsx
var Hello = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
```

The following pattern is not considered warnings:
The following pattern is not considered a warning:

```js
```jsx
const Foo = function(props) {
return <div>{props.foo}</div>;
};
```

The following pattern is not considered warning in React <15.0.0:
The following pattern is not considered a warning in React <15.0.0:

```js
```jsx
class Foo extends React.Component {
render() {
if (!this.props.foo) {
Expand All @@ -43,3 +44,39 @@ class Foo extends React.Component {
}
}
```


## Rule Options

```js
...
"prefer-stateless-function": [<enabled>, { "ignorePureComponent": <ignorePureComponent> }]
...
```

* `enabled`: for enabling the rule. 0=off, 1=warn, 2=error. Defaults to 0.
* `ignorePureComponent`: optional boolean set to `true` to ignore components extending from `React.PureComponent` (default to `false`).

### `ignorePureComponent`

When `true` the rule will ignore Components extending from `React.PureComponent` that use `this.props` or `this.context`.

The following patterns is considered okay and does not cause warnings:

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

The following pattern is considered a warning because it's not using props or context:

```jsx
class Foo extends React.PureComponent {
render() {
return <div>Bar</div>;
}
}
```
51 changes: 51 additions & 0 deletions docs/rules/style-prop-object.md
@@ -0,0 +1,51 @@
# Enforce style prop value being an object (style-prop-object)

Require that the value of the prop `style` be an object or a variable that is
an object.

## Rule Details

The following patterns are considered warnings:

```jsx
<div style="color: 'red'" />

<div style={true} />

<Hello style={true} />

const styles = true;
<div style={styles} />
```

```js
React.createElement("div", { style: "color: 'red'" });

React.createElement("div", { style: true });

React.createElement("Hello", { style: true });

const styles = true;
React.createElement("div", { style: styles });
```


The following patterns are not considered warnings:

```jsx
<div style={{ color: "red" }} />

<Hello style={{ color: "red" }} />

const styles = { color: "red" };
<div style={styles} />
```

```js
React.createElement("div", { style: { color: 'red' }});

React.createElement("Hello", { style: { color: 'red' }});

const styles = { height: '100px' };
React.createElement("div", { style: styles });
```
1 change: 1 addition & 0 deletions index.js
Expand Up @@ -55,6 +55,7 @@ var rules = {
'require-optimization': require('./lib/rules/require-optimization'),
'no-find-dom-node': require('./lib/rules/no-find-dom-node'),
'no-danger-with-children': require('./lib/rules/no-danger-with-children'),
'style-prop-object': require('./lib/rules/style-prop-object'),
'no-unused-prop-types': require('./lib/rules/no-unused-prop-types')
};

Expand Down
7 changes: 0 additions & 7 deletions lib/rules/jsx-uses-vars.js
Expand Up @@ -23,13 +23,6 @@ module.exports = {
create: function(context) {

return {
JSXExpressionContainer: function(node) {
if (node.expression.type !== 'Identifier') {
return;
}
variableUtil.markVariableAsUsed(context, node.expression.name);
},

JSXOpeningElement: function(node) {
var name;
if (node.name.namespace && node.name.namespace.name) {
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/no-multi-comp.js
Expand Up @@ -43,7 +43,7 @@ module.exports = {
* @returns {Boolean} True if the component is ignored, false if not.
*/
function isIgnored(component) {
return ignoreStateless === true && /Function/.test(component.node.type);
return ignoreStateless && /Function/.test(component.node.type);
}

// --------------------------------------------------------------------------
Expand Down
51 changes: 48 additions & 3 deletions lib/rules/prefer-stateless-function.js
Expand Up @@ -20,11 +20,23 @@ module.exports = {
category: 'Stylistic Issues',
recommended: false
},
schema: []
schema: [{
type: 'object',
properties: {
ignorePureComponents: {
default: false,
type: 'boolean'
}
},
additionalProperties: false
}]
},

create: Components.detect(function(context, components, utils) {

var configuration = context.options[0] || {};
var ignorePureComponents = configuration.ignorePureComponents || false;

var sourceCode = context.getSourceCode();

// --------------------------------------------------------------------------
Expand Down Expand Up @@ -213,6 +225,16 @@ module.exports = {
});
}

/**
* Mark component as pure as declared
* @param {ASTNode} node The AST node being checked.
*/
var markSCUAsDeclared = function (node) {
components.set(node, {
hasSCU: true
});
};

/**
* Mark a setState as used
* @param {ASTNode} node The AST node being checked.
Expand All @@ -223,6 +245,16 @@ module.exports = {
});
}

/**
* Mark a props or context as used
* @param {ASTNode} node The AST node being checked.
*/
function markPropsOrContextAsUsed(node) {
components.set(node, {
usePropsOrContext: true
});
}

/**
* Mark a ref as used
* @param {ASTNode} node The AST node being checked.
Expand All @@ -244,6 +276,12 @@ module.exports = {
}

return {
ClassDeclaration: function (node) {
if (ignorePureComponents && utils.isPureComponent(node)) {
markSCUAsDeclared(node);
}
},

// Mark `this` destructuring as a usage of `this`
VariableDeclarator: function(node) {
// Ignore destructuring on other than `this`
Expand All @@ -256,6 +294,7 @@ module.exports = {
return name !== 'props' && name !== 'context';
});
if (!useThis) {
markPropsOrContextAsUsed(node);
return;
}
markThisAsUsed(node);
Expand All @@ -264,11 +303,13 @@ module.exports = {
// Mark `this` usage
MemberExpression: function(node) {
// Ignore calls to `this.props` and `this.context`
if (
node.object.type !== 'ThisExpression' ||
if (node.object.type !== 'ThisExpression') {
return;
} else if (
(node.property.name || node.property.value) === 'props' ||
(node.property.name || node.property.value) === 'context'
) {
markPropsOrContextAsUsed(node);
return;
}
markThisAsUsed(node);
Expand Down Expand Up @@ -322,6 +363,10 @@ module.exports = {
continue;
}

if (list[component].hasSCU && list[component].usePropsOrContext) {
continue;
}

context.report({
node: list[component].node,
message: 'Component should be written as a pure function'
Expand Down
22 changes: 2 additions & 20 deletions lib/rules/require-optimization.js
Expand Up @@ -5,7 +5,6 @@
'use strict';

var Components = require('../util/Components');
var pragmaUtil = require('../util/pragma');

module.exports = {
meta: {
Expand All @@ -29,15 +28,11 @@ module.exports = {
}]
},

create: Components.detect(function (context, components) {
create: Components.detect(function (context, components, utils) {
var MISSING_MESSAGE = 'Component is not optimized. Please add a shouldComponentUpdate method.';
var configuration = context.options[0] || {};
var allowDecorators = configuration.allowDecorators || [];

var pragma = pragmaUtil.getFromContext(context);
var pureComponentRegExp = new RegExp('^(' + pragma + '\\.)?PureComponent$');
var sourceCode = context.getSourceCode();

/**
* Checks to see if our component is decorated by PureRenderMixin via reactMixin
* @param {ASTNode} node The AST node being checked.
Expand Down Expand Up @@ -89,19 +84,6 @@ module.exports = {
return false;
};

/**
* Checks to see if our component extends React.PureComponent
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean} True if node extends React.PureComponent, false if not.
*/
var isPureComponent = function (node) {
if (node.superClass) {
return pureComponentRegExp.test(sourceCode.getText(node.superClass));
}

return false;
};

/**
* Checks if we are declaring a shouldComponentUpdate method
* @param {ASTNode} node The AST node being checked.
Expand Down Expand Up @@ -186,7 +168,7 @@ module.exports = {
},

ClassDeclaration: function (node) {
if (!(hasPureRenderDecorator(node) || hasCustomDecorator(node) || isPureComponent(node))) {
if (!(hasPureRenderDecorator(node) || hasCustomDecorator(node) || utils.isPureComponent(node))) {
return;
}
markSCUAsDeclared(node);
Expand Down

0 comments on commit b2c8cf5

Please sign in to comment.