Skip to content

Commit

Permalink
add forbid-prop-types rule that by default forbids any, array, and ob…
Browse files Browse the repository at this point in the history
…ject
  • Loading branch information
pwmckenna committed Sep 23, 2015
1 parent 87430bd commit 0eb53a3
Show file tree
Hide file tree
Showing 4 changed files with 602 additions and 2 deletions.
57 changes: 57 additions & 0 deletions docs/rules/forbid-prop-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Forbid certain propTypes (forbid-prop-types)

By default this rule prevents vague prop types with more specific alternatives available (`any`, `array`, `object`), but any prop type can be disabled if desired. The defaults are chosen because they have obvious replacements. `any` should be replaced with, well, anything. `array` and `object` can be replaced with `arrayOf` and `shape`, respectively.

## Rule Details

This rule checks all JSX components and verifies that no forbidden propsTypes are used.
This rule is off by default.

The following patterns are considered warnings:

```js
var Component = React.createClass({
propTypes: {
a: React.PropTypes.any,
r: React.PropTypes.array,
o: React.PropTypes.object
},
...
});

class Component extends React.Component {
...
}
Component.propTypes = {
a: React.PropTypes.any,
r: React.PropTypes.array,
o: React.PropTypes.object
};

class Component extends React.Component {
static propTypes = {
a: React.PropTypes.any,
r: React.PropTypes.array,
o: React.PropTypes.object
}
render() {
return <div />;
}
}
```

## Rule Options

```js
...
"forbid-prop-types": [<enabled>, { "forbid": [<string>] }]
...
```

### `forbid`

An array of strings, with the names of React.PropType keys that are forbidden.

## When not to use

This rule is a formatting/documenting preference and not following it won't negatively affect the quality of your code. This rule encourages prop types that more specifically document their usage.
6 changes: 4 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ module.exports = {
'jsx-no-literals': require('./lib/rules/jsx-no-literals'),
'jsx-indent-props': require('./lib/rules/jsx-indent-props'),
'jsx-closing-bracket-location': require('./lib/rules/jsx-closing-bracket-location'),
'no-direct-mutation-state': require('./lib/rules/no-direct-mutation-state')
'no-direct-mutation-state': require('./lib/rules/no-direct-mutation-state'),
'forbid-prop-types': require('./lib/rules/forbid-prop-types')
},
rulesConfig: {
'jsx-uses-react': 0,
Expand Down Expand Up @@ -57,6 +58,7 @@ module.exports = {
'jsx-no-literals': 0,
'jsx-indent-props': 0,
'jsx-closing-bracket-location': 0,
'no-direct-mutation-state': 0
'no-direct-mutation-state': 0,
'forbid-prop-types': 0
}
};
116 changes: 116 additions & 0 deletions lib/rules/forbid-prop-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* @fileoverview Forbid certain propTypes
*/
'use strict';

// ------------------------------------------------------------------------------
// Constants
// ------------------------------------------------------------------------------

var DEFAULTS = ['any', 'array', 'object'];

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = function(context) {

function isForbidden(type) {
var configuration = context.options[0] || {};

var forbid = configuration.forbid || DEFAULTS;
return forbid.indexOf(type) >= 0;
}

/**
* Checks if node is `propTypes` declaration
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean} True if node is `propTypes` declaration, false if not.
*/
function isPropTypesDeclaration(node) {

// Special case for class properties
// (babel-eslint does not expose property name so we have to rely on tokens)
if (node.type === 'ClassProperty') {
var tokens = context.getFirstTokens(node, 2);
if (tokens[0].value === 'propTypes' || tokens[1].value === 'propTypes') {
return true;
}
return false;
}

return Boolean(
node &&
node.name === 'propTypes'
);
}


/**
* Checks if propTypes declarations are forbidden
* @param {Array} declarations The array of AST nodes being checked.
* @returns {void}
*/
function checkForbidden(declarations) {
declarations.forEach(function(declaration) {
if (
declaration.value.type === 'MemberExpression' &&
declaration.value.property &&
declaration.value.property.name &&
declaration.value.property.name === 'isRequired'
) {
declaration.value = declaration.value.object;
}

if (isForbidden(declaration.value.property.name)) {
context.report(declaration, 'Prop type `' + declaration.value.property.name + '` is forbidden');
}
});
}

return {
ClassProperty: function(node) {
if (isPropTypesDeclaration(node) && node.value && node.value.type === 'ObjectExpression') {
checkForbidden(node.value.properties);
}
},

MemberExpression: function(node) {
if (isPropTypesDeclaration(node.property)) {
var right = node.parent.right;
if (right && right.type === 'ObjectExpression') {
checkForbidden(right.properties);
}
}
},

ObjectExpression: function(node) {
node.properties.forEach(function(property) {
if (!property.key) {
return;
}

if (!isPropTypesDeclaration(property.key)) {
return;
}
if (property.value.type === 'ObjectExpression') {
checkForbidden(property.value.properties);
}
});
}

};
};

module.exports.schema = [{
type: 'object',
properties: {
forbid: {
type: 'array',
items: {
type: 'string'
}
}
},
additionalProperties: true
}];

0 comments on commit 0eb53a3

Please sign in to comment.