Skip to content

Commit c791caa

Browse files
committed
Merge pull request #225 from pwmckenna/forbid-prop-types
Add forbid-prop-types rule (fixes #215)
2 parents 87430bd + 0eb53a3 commit c791caa

File tree

4 files changed

+602
-2
lines changed

4 files changed

+602
-2
lines changed

docs/rules/forbid-prop-types.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Forbid certain propTypes (forbid-prop-types)
2+
3+
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.
4+
5+
## Rule Details
6+
7+
This rule checks all JSX components and verifies that no forbidden propsTypes are used.
8+
This rule is off by default.
9+
10+
The following patterns are considered warnings:
11+
12+
```js
13+
var Component = React.createClass({
14+
propTypes: {
15+
a: React.PropTypes.any,
16+
r: React.PropTypes.array,
17+
o: React.PropTypes.object
18+
},
19+
...
20+
});
21+
22+
class Component extends React.Component {
23+
...
24+
}
25+
Component.propTypes = {
26+
a: React.PropTypes.any,
27+
r: React.PropTypes.array,
28+
o: React.PropTypes.object
29+
};
30+
31+
class Component extends React.Component {
32+
static propTypes = {
33+
a: React.PropTypes.any,
34+
r: React.PropTypes.array,
35+
o: React.PropTypes.object
36+
}
37+
render() {
38+
return <div />;
39+
}
40+
}
41+
```
42+
43+
## Rule Options
44+
45+
```js
46+
...
47+
"forbid-prop-types": [<enabled>, { "forbid": [<string>] }]
48+
...
49+
```
50+
51+
### `forbid`
52+
53+
An array of strings, with the names of React.PropType keys that are forbidden.
54+
55+
## When not to use
56+
57+
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.

index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ module.exports = {
2828
'jsx-no-literals': require('./lib/rules/jsx-no-literals'),
2929
'jsx-indent-props': require('./lib/rules/jsx-indent-props'),
3030
'jsx-closing-bracket-location': require('./lib/rules/jsx-closing-bracket-location'),
31-
'no-direct-mutation-state': require('./lib/rules/no-direct-mutation-state')
31+
'no-direct-mutation-state': require('./lib/rules/no-direct-mutation-state'),
32+
'forbid-prop-types': require('./lib/rules/forbid-prop-types')
3233
},
3334
rulesConfig: {
3435
'jsx-uses-react': 0,
@@ -57,6 +58,7 @@ module.exports = {
5758
'jsx-no-literals': 0,
5859
'jsx-indent-props': 0,
5960
'jsx-closing-bracket-location': 0,
60-
'no-direct-mutation-state': 0
61+
'no-direct-mutation-state': 0,
62+
'forbid-prop-types': 0
6163
}
6264
};

lib/rules/forbid-prop-types.js

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* @fileoverview Forbid certain propTypes
3+
*/
4+
'use strict';
5+
6+
// ------------------------------------------------------------------------------
7+
// Constants
8+
// ------------------------------------------------------------------------------
9+
10+
var DEFAULTS = ['any', 'array', 'object'];
11+
12+
// ------------------------------------------------------------------------------
13+
// Rule Definition
14+
// ------------------------------------------------------------------------------
15+
16+
module.exports = function(context) {
17+
18+
function isForbidden(type) {
19+
var configuration = context.options[0] || {};
20+
21+
var forbid = configuration.forbid || DEFAULTS;
22+
return forbid.indexOf(type) >= 0;
23+
}
24+
25+
/**
26+
* Checks if node is `propTypes` declaration
27+
* @param {ASTNode} node The AST node being checked.
28+
* @returns {Boolean} True if node is `propTypes` declaration, false if not.
29+
*/
30+
function isPropTypesDeclaration(node) {
31+
32+
// Special case for class properties
33+
// (babel-eslint does not expose property name so we have to rely on tokens)
34+
if (node.type === 'ClassProperty') {
35+
var tokens = context.getFirstTokens(node, 2);
36+
if (tokens[0].value === 'propTypes' || tokens[1].value === 'propTypes') {
37+
return true;
38+
}
39+
return false;
40+
}
41+
42+
return Boolean(
43+
node &&
44+
node.name === 'propTypes'
45+
);
46+
}
47+
48+
49+
/**
50+
* Checks if propTypes declarations are forbidden
51+
* @param {Array} declarations The array of AST nodes being checked.
52+
* @returns {void}
53+
*/
54+
function checkForbidden(declarations) {
55+
declarations.forEach(function(declaration) {
56+
if (
57+
declaration.value.type === 'MemberExpression' &&
58+
declaration.value.property &&
59+
declaration.value.property.name &&
60+
declaration.value.property.name === 'isRequired'
61+
) {
62+
declaration.value = declaration.value.object;
63+
}
64+
65+
if (isForbidden(declaration.value.property.name)) {
66+
context.report(declaration, 'Prop type `' + declaration.value.property.name + '` is forbidden');
67+
}
68+
});
69+
}
70+
71+
return {
72+
ClassProperty: function(node) {
73+
if (isPropTypesDeclaration(node) && node.value && node.value.type === 'ObjectExpression') {
74+
checkForbidden(node.value.properties);
75+
}
76+
},
77+
78+
MemberExpression: function(node) {
79+
if (isPropTypesDeclaration(node.property)) {
80+
var right = node.parent.right;
81+
if (right && right.type === 'ObjectExpression') {
82+
checkForbidden(right.properties);
83+
}
84+
}
85+
},
86+
87+
ObjectExpression: function(node) {
88+
node.properties.forEach(function(property) {
89+
if (!property.key) {
90+
return;
91+
}
92+
93+
if (!isPropTypesDeclaration(property.key)) {
94+
return;
95+
}
96+
if (property.value.type === 'ObjectExpression') {
97+
checkForbidden(property.value.properties);
98+
}
99+
});
100+
}
101+
102+
};
103+
};
104+
105+
module.exports.schema = [{
106+
type: 'object',
107+
properties: {
108+
forbid: {
109+
type: 'array',
110+
items: {
111+
type: 'string'
112+
}
113+
}
114+
},
115+
additionalProperties: true
116+
}];

0 commit comments

Comments
 (0)