-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Add forbid-foreign-prop-types rule #1055
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add forbid-foreign-prop-types rule #1055
Conversation
@@ -0,0 +1,28 @@ | |||
# Forbid foreign propTypes (forbid-foreign-prop-types) | |||
|
|||
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 to remove propTypes from their components in production builds, to do so safely. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to link to the babel plugin here.
The following patterns are not considered warnings: | ||
|
||
```js | ||
import {propTypes: someComponentPropTypes}, SomeComponent from './SomeComponent'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, even importing like this does not ensure that propTypes
is explicitly exported from the module (and not just a property on the exported object).
What do you think about making this rule additionally ensure that propTypes is explicitly exported when being imported as well? Alternatively, if there is a rule in eslint-plugin-import that covers this, we should mention it here.
Also, this line should be
import SomeComponent, {propTypes as someComponentPropTypes} from './SomeComponent';
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lencioni Looks like eslint-plugin-import has the named
rule which would ensure that named imports are also named exports: https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/named.md
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great! Will you please add a note about this and a link to the rule in the documentation?
d7f5e49
to
f5cc025
Compare
}, | ||
|
||
ObjectPattern: function(node) { | ||
var propTypesNode = node.properties.find(function(property) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eslint-plugin-react still supports node 0.10, so please use array.prototype.find
here instead (after this is merged, i'll update #1038 to switch back to .find
)
} | ||
}; | ||
|
||
require('babel-eslint'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
none of the code in this test requires object rest/spread or babel-eslint; could these tests just stick with espree?
MemberExpression: function(node) { | ||
if (node.property && node.property.type === 'Identifier' && node.property.name === 'propTypes' || | ||
node.property && node.property.type === 'Literal' && node.property.value === 'propTypes') { | ||
context.report({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it'd be helpful if the error could include the position of the warning, so that editors can jump the cursor right to it
}, { | ||
code: 'import { propTypes as someComponentPropTypes } from "SomeComponent";', | ||
parser: 'babel-eslint' | ||
}], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be worth adding some valid cases like:
const foo = propTypes
foo(propTypes)
foo + propTypes
const foo = [propTypes]
const foo = { propTypes }
And maybe even junk like this (note the lowercase):
foo.propTypes
foo['propTypes']
const { propTypes } = foo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why lowercase matters - I agree that cases where it's an object or array literal should be valid, but class foo extends React.Component
should have the same restrictions (someone could always import Foo from './foo'
and use it just fine)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this plugin already has some component detection logic we could reuse?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking lowercase should perhaps be allowed because in React, components must be uppercase. Lowercase means DOM element. But this distinction is unlikely to matter much here. Reusing component detection logic sounds good to me.
@iancmyers Thanks for working on that! react-native is encouraging the following pattern. We might need an exception. import {View} from 'react-native';
class CustomView extends React.Component {
static propTypes = {
style: View.propTypes.style
};
render() {
return (
<View style={this.props.style}>..</View>
);
}
} |
That's just static class properties (which are a stage 2 proposal) - definitely those should be covered as well, but since it's just sugar for |
66e8e24
to
6241f4b
Compare
People may want to use babel-plugin-transform-react-remove-prop-types to remove propTypes from their components in production builds, as an optimization. The `forbib-foreign-prop-types` rule forbids using another component's prop types unless they are explicitly imported/exported, which makes that optimization less prone to error. Fixes jsx-eslint#696
6241f4b
to
0b25b66
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
// -------------------------------------------------------------------------- | ||
|
||
function isLeftSideOfAssignment(node) { | ||
return node.parent.type === 'AssignmentExpression' && node.parent.left === node; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will a node always have a .parent
property?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that MemberExpression
and ObjectPattern
will always at least have a parent of Program
.
@iancmyers thanks for your contribution! @oliviertassinari Thanks for raising this scenario. I'm going to merge this as it is, and we can follow up with an improvement to address that if we need to. One thought is to allow referencing propTypes on objects imported from packages, but we can discuss the details in a separate issue or PR. |
People may want to use babel-plugin-transform-react-remove-prop-types to remove propTypes from their components in production builds, as an optimization. The
forbib-foreign-prop-types
rule forbids using another component's prop types unless they are explicitly imported/exported, which makes that optimization less prone to error.I've included the use-cases described in #696 as tests. I'd be happy to add additional test cases if recommended.
Fixes #696