Skip to content

Commit 98b047d

Browse files
iansuljharb
authored andcommitted
[New] forbid-foreign-prop-types: Add allowInPropTypes option
Fixes #1647.
1 parent f7441bb commit 98b047d

File tree

2 files changed

+95
-9
lines changed

2 files changed

+95
-9
lines changed

lib/rules/forbid-foreign-prop-types.js

+60-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ const docsUrl = require('../util/docsUrl');
1010
// Constants
1111
// ------------------------------------------------------------------------------
1212

13-
1413
// ------------------------------------------------------------------------------
1514
// Rule Definition
1615
// ------------------------------------------------------------------------------
@@ -22,10 +21,25 @@ module.exports = {
2221
category: 'Best Practices',
2322
recommended: false,
2423
url: docsUrl('forbid-foreign-prop-types')
25-
}
24+
},
25+
26+
schema: [
27+
{
28+
type: 'object',
29+
properties: {
30+
allowInPropTypes: {
31+
type: 'boolean'
32+
}
33+
},
34+
additionalProperties: false
35+
}
36+
]
2637
},
2738

2839
create: function(context) {
40+
const config = context.options[0] || {};
41+
const allowInPropTypes = config.allowInPropTypes || false;
42+
2943
// --------------------------------------------------------------------------
3044
// Helpers
3145
// --------------------------------------------------------------------------
@@ -34,15 +48,53 @@ module.exports = {
3448
return node.parent.type === 'AssignmentExpression' && node.parent.left === node;
3549
}
3650

51+
function findParentAssignmentExpression(node) {
52+
let parent = node.parent;
53+
54+
while (parent && parent.type !== 'Program') {
55+
if (parent.type === 'AssignmentExpression') {
56+
return parent;
57+
}
58+
parent = parent.parent;
59+
}
60+
return null;
61+
}
62+
63+
function isAllowedAssignment(node) {
64+
if (!allowInPropTypes) {
65+
return false;
66+
}
67+
68+
const assignmentExpression = findParentAssignmentExpression(node);
69+
70+
if (assignmentExpression &&
71+
assignmentExpression.left &&
72+
assignmentExpression.left.property &&
73+
assignmentExpression.left.property.name === 'propTypes'
74+
) {
75+
return true;
76+
}
77+
return false;
78+
}
79+
3780
return {
3881
MemberExpression: function(node) {
39-
if (!node.computed && node.property && node.property.type === 'Identifier' &&
40-
node.property.name === 'propTypes' && !isLeftSideOfAssignment(node) ||
41-
node.property && node.property.type === 'Literal' &&
42-
node.property.value === 'propTypes' && !isLeftSideOfAssignment(node)) {
82+
if (
83+
(!node.computed &&
84+
node.property &&
85+
node.property.type === 'Identifier' &&
86+
node.property.name === 'propTypes' &&
87+
!isLeftSideOfAssignment(node) &&
88+
!isAllowedAssignment(node)) ||
89+
(node.property &&
90+
node.property.type === 'Literal' &&
91+
node.property.value === 'propTypes' &&
92+
!isLeftSideOfAssignment(node) &&
93+
!isAllowedAssignment(node))
94+
) {
4395
context.report({
4496
node: node.property,
45-
message: 'Using another component\'s propTypes is forbidden'
97+
message: 'Using propTypes from another component is not safe because they may be removed in production builds'
4698
});
4799
}
48100
},
@@ -53,7 +105,7 @@ module.exports = {
53105
if (propTypesNode) {
54106
context.report({
55107
node: propTypesNode,
56-
message: 'Using another component\'s propTypes is forbidden'
108+
message: 'Using propTypes from another component is not safe because they may be removed in production builds'
57109
});
58110
}
59111
}

tests/lib/rules/forbid-foreign-prop-types.js

+35-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ require('babel-eslint');
2525
// Tests
2626
// -----------------------------------------------------------------------------
2727

28-
const ERROR_MESSAGE = 'Using another component\'s propTypes is forbidden';
28+
const ERROR_MESSAGE = 'Using propTypes from another component is not safe because they may be removed in production builds';
2929

3030
const ruleTester = new RuleTester({parserOptions});
3131
ruleTester.run('forbid-foreign-prop-types', rule, {
@@ -50,6 +50,21 @@ ruleTester.run('forbid-foreign-prop-types', rule, {
5050
code: 'Foo["propTypes"] = propTypes'
5151
}, {
5252
code: 'const propTypes = "bar"; Foo[propTypes];'
53+
},
54+
{
55+
code: `
56+
const Message = (props) => (<div>{props.message}</div>);
57+
Message.propTypes = {
58+
message: PropTypes.string
59+
};
60+
const Hello = (props) => (<Message>Hello {props.name}</Message>);
61+
Hello.propTypes = {
62+
name: Message.propTypes.message
63+
};
64+
`,
65+
options: [{
66+
allowInPropTypes: true
67+
}]
5368
}],
5469

5570
invalid: [{
@@ -139,5 +154,24 @@ ruleTester.run('forbid-foreign-prop-types', rule, {
139154
message: ERROR_MESSAGE,
140155
type: 'Property'
141156
}]
157+
},
158+
{
159+
code: `
160+
const Message = (props) => (<div>{props.message}</div>);
161+
Message.propTypes = {
162+
message: PropTypes.string
163+
};
164+
const Hello = (props) => (<Message>Hello {props.name}</Message>);
165+
Hello.propTypes = {
166+
name: Message.propTypes.message
167+
};
168+
`,
169+
options: [{
170+
allowInPropTypes: false
171+
}],
172+
errors: [{
173+
message: ERROR_MESSAGE,
174+
type: 'Identifier'
175+
}]
142176
}]
143177
});

0 commit comments

Comments
 (0)