Skip to content

Commit 006441f

Browse files
authored
Merge pull request #1533 from jomasti/issue-1513
forbid-prop-types: forbid contextTypes, childContextTypes
2 parents 72d7815 + 0ab4b82 commit 006441f

File tree

6 files changed

+1208
-159
lines changed

6 files changed

+1208
-159
lines changed

docs/rules/forbid-prop-types.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,22 @@ class Component extends React.Component {
4444

4545
```js
4646
...
47-
"react/forbid-prop-types": [<enabled>, { "forbid": [<string>] }]
47+
"react/forbid-prop-types": [<enabled>, { "forbid": [<string>], checkContextTypes: <boolean>, checkChildContextTypes: <boolean> }]
4848
...
4949
```
5050

5151
### `forbid`
5252

5353
An array of strings, with the names of `PropTypes` keys that are forbidden. The default value for this option is `['any', 'array', 'object']`.
5454

55+
### `checkContextTypes`
56+
57+
Whether or not to check `contextTypes` for forbidden prop types. The default value is false.
58+
59+
### `checkChildContextTypes`
60+
61+
Whether or not to check `childContextTypes` for forbidden prop types. The default value is false.
62+
5563
## When not to use
5664

5765
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.

lib/rules/forbid-prop-types.js

+55-5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
const variableUtil = require('../util/variable');
77
const propsUtil = require('../util/props');
8+
const astUtil = require('../util/ast');
89

910
// ------------------------------------------------------------------------------
1011
// Constants
@@ -32,6 +33,12 @@ module.exports = {
3233
items: {
3334
type: 'string'
3435
}
36+
},
37+
checkContextTypes: {
38+
type: 'boolean'
39+
},
40+
checkChildContextTypes: {
41+
type: 'boolean'
3542
}
3643
},
3744
additionalProperties: true
@@ -40,14 +47,29 @@ module.exports = {
4047

4148
create: function(context) {
4249
const propWrapperFunctions = new Set(context.settings.propWrapperFunctions || []);
50+
const configuration = context.options[0] || {};
51+
const checkContextTypes = configuration.checkContextTypes || false;
52+
const checkChildContextTypes = configuration.checkChildContextTypes || false;
4353

4454
function isForbidden(type) {
45-
const configuration = context.options[0] || {};
46-
4755
const forbid = configuration.forbid || DEFAULTS;
4856
return forbid.indexOf(type) >= 0;
4957
}
5058

59+
function shouldCheckContextTypes(node) {
60+
if (checkContextTypes && propsUtil.isContextTypesDeclaration(node)) {
61+
return true;
62+
}
63+
return false;
64+
}
65+
66+
function shouldCheckChildContextTypes(node) {
67+
if (checkChildContextTypes && propsUtil.isChildContextTypesDeclaration(node)) {
68+
return true;
69+
}
70+
return false;
71+
}
72+
5173
/**
5274
* Find a variable by name in the current scope.
5375
* @param {string} name Name of the variable to look for.
@@ -132,27 +154,55 @@ module.exports = {
132154

133155
return {
134156
ClassProperty: function(node) {
135-
if (!propsUtil.isPropTypesDeclaration(node)) {
157+
if (
158+
!propsUtil.isPropTypesDeclaration(node) &&
159+
!shouldCheckContextTypes(node) &&
160+
!shouldCheckChildContextTypes(node)
161+
) {
136162
return;
137163
}
138164
checkNode(node.value);
139165
},
140166

141167
MemberExpression: function(node) {
142-
if (!propsUtil.isPropTypesDeclaration(node)) {
168+
if (
169+
!propsUtil.isPropTypesDeclaration(node) &&
170+
!shouldCheckContextTypes(node) &&
171+
!shouldCheckChildContextTypes(node)
172+
) {
143173
return;
144174
}
145175

146176
checkNode(node.parent.right);
147177
},
148178

179+
MethodDefinition: function(node) {
180+
if (
181+
!propsUtil.isPropTypesDeclaration(node) &&
182+
!shouldCheckContextTypes(node) &&
183+
!shouldCheckChildContextTypes(node)
184+
) {
185+
return;
186+
}
187+
188+
const returnStatement = astUtil.findReturnStatement(node);
189+
190+
if (returnStatement && returnStatement.argument) {
191+
checkNode(returnStatement.argument);
192+
}
193+
},
194+
149195
ObjectExpression: function(node) {
150196
node.properties.forEach(property => {
151197
if (!property.key) {
152198
return;
153199
}
154200

155-
if (!propsUtil.isPropTypesDeclaration(property)) {
201+
if (
202+
!propsUtil.isPropTypesDeclaration(property) &&
203+
!shouldCheckContextTypes(property) &&
204+
!shouldCheckChildContextTypes(property)
205+
) {
156206
return;
157207
}
158208
if (property.value.type === 'ObjectExpression') {

lib/util/Components.js

+2-18
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const util = require('util');
99
const doctrine = require('doctrine');
1010
const variableUtil = require('./variable');
1111
const pragmaUtil = require('./pragma');
12+
const astUtil = require('./ast');
1213

1314
const usedPropTypesAreEquivalent = (propA, propB) => {
1415
if (propA.name === propB.name) {
@@ -356,24 +357,7 @@ function componentRule(rule, context) {
356357
*
357358
* @param {ASTNode} ASTnode The AST node being checked
358359
*/
359-
findReturnStatement: function(node) {
360-
if (
361-
(!node.value || !node.value.body || !node.value.body.body) &&
362-
(!node.body || !node.body.body)
363-
) {
364-
return false;
365-
}
366-
367-
const bodyNodes = (node.value ? node.value.body.body : node.body.body);
368-
369-
let i = bodyNodes.length - 1;
370-
for (; i >= 0; i--) {
371-
if (bodyNodes[i].type === 'ReturnStatement') {
372-
return bodyNodes[i];
373-
}
374-
}
375-
return false;
376-
},
360+
findReturnStatement: astUtil.findReturnStatement,
377361

378362
/**
379363
* Get the parent component node from the current scope

lib/util/ast.js

+26-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,32 @@ function getComponentProperties(node) {
3535
}
3636
}
3737

38+
/**
39+
* Find a return statment in the current node
40+
*
41+
* @param {ASTNode} ASTnode The AST node being checked
42+
*/
43+
function findReturnStatement(node) {
44+
if (
45+
(!node.value || !node.value.body || !node.value.body.body) &&
46+
(!node.body || !node.body.body)
47+
) {
48+
return false;
49+
}
50+
51+
const bodyNodes = (node.value ? node.value.body.body : node.body.body);
52+
53+
let i = bodyNodes.length - 1;
54+
for (; i >= 0; i--) {
55+
if (bodyNodes[i].type === 'ReturnStatement') {
56+
return bodyNodes[i];
57+
}
58+
}
59+
return false;
60+
}
61+
3862
module.exports = {
3963
getPropertyName: getPropertyName,
40-
getComponentProperties: getComponentProperties
64+
getComponentProperties: getComponentProperties,
65+
findReturnStatement: findReturnStatement
4166
};

lib/util/props.js

+26
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,30 @@ function isPropTypesDeclaration(node) {
2020
return astUtil.getPropertyName(node) === 'propTypes';
2121
}
2222

23+
/**
24+
* Checks if the node passed in looks like a contextTypes declaration.
25+
* @param {ASTNode} node The node to check.
26+
* @returns {Boolean} `true` if the node is a contextTypes declaration, `false` if not
27+
*/
28+
function isContextTypesDeclaration(node) {
29+
if (node && node.type === 'ClassProperty') {
30+
// Flow support
31+
if (node.typeAnnotation && node.key.name === 'context') {
32+
return true;
33+
}
34+
}
35+
return astUtil.getPropertyName(node) === 'contextTypes';
36+
}
37+
38+
/**
39+
* Checks if the node passed in looks like a childContextTypes declaration.
40+
* @param {ASTNode} node The node to check.
41+
* @returns {Boolean} `true` if the node is a childContextTypes declaration, `false` if not
42+
*/
43+
function isChildContextTypesDeclaration(node) {
44+
return astUtil.getPropertyName(node) === 'childContextTypes';
45+
}
46+
2347
/**
2448
* Checks if the Identifier node passed in looks like a defaultProps declaration.
2549
* @param {ASTNode} node The node to check. Must be an Identifier node.
@@ -41,6 +65,8 @@ function isRequiredPropType(propTypeExpression) {
4165

4266
module.exports = {
4367
isPropTypesDeclaration: isPropTypesDeclaration,
68+
isContextTypesDeclaration: isContextTypesDeclaration,
69+
isChildContextTypesDeclaration: isChildContextTypesDeclaration,
4470
isDefaultPropsDeclaration: isDefaultPropsDeclaration,
4571
isRequiredPropType: isRequiredPropType
4672
};

0 commit comments

Comments
 (0)