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

Lines changed: 1 addition & 0 deletions
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
Lines changed: 30 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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'),
Lines changed: 63 additions & 0 deletions
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+
};
Lines changed: 156 additions & 0 deletions
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)