Skip to content

Commit 54abb8b

Browse files
authored
Merge pull request #1055 from iancmyers/icm-forbid-foreign-prop-type
Add forbid-foreign-prop-types rule
2 parents c586273 + 0b25b66 commit 54abb8b

File tree

5 files changed

+251
-0
lines changed

5 files changed

+251
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#
8383
* [react/display-name](docs/rules/display-name.md): Prevent missing `displayName` in a React component definition
8484
* [react/forbid-component-props](docs/rules/forbid-component-props.md): Forbid certain props on Components
8585
* [react/forbid-prop-types](docs/rules/forbid-prop-types.md): Forbid certain propTypes
86+
* [react/forbid-foreign-prop-types](docs/rules/forbid-foreign-prop-types.md): Forbid foreign propTypes
8687
* [react/no-array-index-key](docs/rules/no-array-index-key.md): Prevent using Array index in `key` props
8788
* [react/no-children-prop](docs/rules/no-children-prop.md): Prevent passing children as props
8889
* [react/no-danger](docs/rules/no-danger.md): Prevent usage of dangerous JSX properties
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Forbid foreign propTypes (forbid-foreign-prop-types)
2+
3+
This rule forbids using another component's prop types unless they are explicitly imported/exported. This allows people who want to use [babel-plugin-transform-react-remove-prop-types](https://github.com/oliviertassinari/babel-plugin-transform-react-remove-prop-types) to remove propTypes from their components in production builds, to do so safely.
4+
5+
In order to ensure that imports are explicitly exported it is recommended to use the ["named" rule in eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/named.md) in conjunction with this rule.
6+
7+
## Rule Details
8+
9+
This rule checks all objects and ensures that the `propTypes` property is not used.
10+
11+
The following patterns are considered warnings:
12+
13+
```js
14+
import SomeComponent from './SomeComponent';
15+
SomeComponent.propTypes;
16+
17+
var { propTypes } = SomeComponent;
18+
19+
SomeComponent['propTypes'];
20+
```
21+
22+
The following patterns are not considered warnings:
23+
24+
```js
25+
import SomeComponent, {propTypes as someComponentPropTypes} from './SomeComponent';
26+
```
27+
28+
## When not to use
29+
30+
This rule aims to make a certain production optimization, removing prop types, less prone to error. This rule may not be relevant to you if you do not wish to make use of this optimization.

index.js

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ var allRules = {
4343
'no-direct-mutation-state': require('./lib/rules/no-direct-mutation-state'),
4444
'forbid-component-props': require('./lib/rules/forbid-component-props'),
4545
'forbid-prop-types': require('./lib/rules/forbid-prop-types'),
46+
'forbid-foreign-prop-types': require('./lib/rules/forbid-foreign-prop-types'),
4647
'prefer-es6-class': require('./lib/rules/prefer-es6-class'),
4748
'jsx-key': require('./lib/rules/jsx-key'),
4849
'no-string-refs': require('./lib/rules/no-string-refs'),
+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* @fileoverview Forbid using another component's propTypes
3+
* @author Ian Christian Myers
4+
*/
5+
'use strict';
6+
7+
var find = require('array.prototype.find');
8+
9+
// ------------------------------------------------------------------------------
10+
// Constants
11+
// ------------------------------------------------------------------------------
12+
13+
14+
// ------------------------------------------------------------------------------
15+
// Rule Definition
16+
// ------------------------------------------------------------------------------
17+
18+
module.exports = {
19+
meta: {
20+
docs: {
21+
description: 'Forbid using another component\'s propTypes',
22+
category: 'Best Practices',
23+
recommended: false
24+
}
25+
},
26+
27+
create: function(context) {
28+
// --------------------------------------------------------------------------
29+
// Helpers
30+
// --------------------------------------------------------------------------
31+
32+
function isLeftSideOfAssignment(node) {
33+
return node.parent.type === 'AssignmentExpression' && node.parent.left === node;
34+
}
35+
36+
return {
37+
MemberExpression: function(node) {
38+
if (!node.computed && node.property && node.property.type === 'Identifier' &&
39+
node.property.name === 'propTypes' && !isLeftSideOfAssignment(node) ||
40+
node.property && node.property.type === 'Literal' &&
41+
node.property.value === 'propTypes' && !isLeftSideOfAssignment(node)) {
42+
context.report({
43+
node: node.property,
44+
message: 'Using another component\'s propTypes is forbidden'
45+
});
46+
}
47+
},
48+
49+
ObjectPattern: function(node) {
50+
var propTypesNode = find(node.properties, function(property) {
51+
return property.type === 'Property' && property.key.name === 'propTypes';
52+
});
53+
54+
if (propTypesNode) {
55+
context.report({
56+
node: propTypesNode,
57+
message: 'Using another component\'s propTypes is forbidden'
58+
});
59+
}
60+
}
61+
};
62+
}
63+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/**
2+
* @fileoverview Tests for forbid-foreign-prop-types
3+
*/
4+
'use strict';
5+
6+
// -----------------------------------------------------------------------------
7+
// Requirements
8+
// -----------------------------------------------------------------------------
9+
10+
var rule = require('../../../lib/rules/forbid-foreign-prop-types');
11+
var RuleTester = require('eslint').RuleTester;
12+
13+
var parserOptions = {
14+
ecmaVersion: 6,
15+
sourceType: 'module',
16+
ecmaFeatures: {
17+
jsx: true
18+
}
19+
};
20+
21+
require('babel-eslint');
22+
23+
// -----------------------------------------------------------------------------
24+
// Tests
25+
// -----------------------------------------------------------------------------
26+
27+
var ERROR_MESSAGE = 'Using another component\'s propTypes is forbidden';
28+
29+
var ruleTester = new RuleTester();
30+
ruleTester.run('forbid-foreign-prop-types', rule, {
31+
32+
valid: [{
33+
code: 'import { propTypes } from "SomeComponent";',
34+
parserOptions: parserOptions
35+
}, {
36+
code: 'import { propTypes as someComponentPropTypes } from "SomeComponent";',
37+
parserOptions: parserOptions
38+
}, {
39+
code: 'const foo = propTypes',
40+
parserOptions: parserOptions
41+
}, {
42+
code: 'foo(propTypes)',
43+
parserOptions: parserOptions
44+
}, {
45+
code: 'foo + propTypes',
46+
parserOptions: parserOptions
47+
}, {
48+
code: 'const foo = [propTypes]',
49+
parserOptions: parserOptions
50+
}, {
51+
code: 'const foo = { propTypes }',
52+
parserOptions: parserOptions
53+
}, {
54+
code: 'Foo.propTypes = propTypes',
55+
parserOptions: parserOptions
56+
}, {
57+
code: 'Foo["propTypes"] = propTypes',
58+
parserOptions: parserOptions
59+
}, {
60+
code: 'const propTypes = "bar"; Foo[propTypes];',
61+
parserOptions: parserOptions
62+
}],
63+
64+
invalid: [{
65+
code: [
66+
'var Foo = React.createClass({',
67+
' propTypes: Bar.propTypes,',
68+
' render: function() {',
69+
' return <Foo className="bar" />;',
70+
' }',
71+
'});'
72+
].join('\n'),
73+
parserOptions: parserOptions,
74+
errors: [{
75+
message: ERROR_MESSAGE,
76+
type: 'Identifier'
77+
}]
78+
},
79+
{
80+
code: [
81+
'var Foo = React.createClass({',
82+
' propTypes: Bar["propTypes"],',
83+
' render: function() {',
84+
' return <Foo className="bar" />;',
85+
' }',
86+
'});'
87+
].join('\n'),
88+
parserOptions: parserOptions,
89+
errors: [{
90+
message: ERROR_MESSAGE,
91+
type: 'Literal'
92+
}]
93+
},
94+
{
95+
code: [
96+
'var { propTypes } = SomeComponent',
97+
'var Foo = React.createClass({',
98+
' propTypes,',
99+
' render: function() {',
100+
' return <Foo className="bar" />;',
101+
' }',
102+
'});'
103+
].join('\n'),
104+
parserOptions: parserOptions,
105+
errors: [{
106+
message: ERROR_MESSAGE,
107+
type: 'Property'
108+
}]
109+
},
110+
{
111+
code: [
112+
'var { propTypes: things, ...foo } = SomeComponent',
113+
'var Foo = React.createClass({',
114+
' propTypes,',
115+
' render: function() {',
116+
' return <Foo className="bar" />;',
117+
' }',
118+
'});'
119+
].join('\n'),
120+
parser: 'babel-eslint',
121+
errors: [{
122+
message: ERROR_MESSAGE,
123+
type: 'Property'
124+
}]
125+
},
126+
{
127+
code: [
128+
'class MyComponent extends React.Component {',
129+
' static fooBar = {',
130+
' baz: Qux.propTypes.baz',
131+
' };',
132+
'}'
133+
].join('\n'),
134+
parser: 'babel-eslint',
135+
errors: [{
136+
message: ERROR_MESSAGE,
137+
type: 'Identifier'
138+
}]
139+
},
140+
{
141+
code: [
142+
'var { propTypes: typesOfProps } = SomeComponent',
143+
'var Foo = React.createClass({',
144+
' propTypes: typesOfProps,',
145+
' render: function() {',
146+
' return <Foo className="bar" />;',
147+
' }',
148+
'});'
149+
].join('\n'),
150+
parserOptions: parserOptions,
151+
errors: [{
152+
message: ERROR_MESSAGE,
153+
type: 'Property'
154+
}]
155+
}]
156+
});

0 commit comments

Comments
 (0)