Skip to content
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

Merged
merged 1 commit into from Feb 14, 2017

Conversation

iancmyers
Copy link
Contributor

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

@@ -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.
Copy link
Collaborator

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';
Copy link
Collaborator

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';

Copy link
Contributor Author

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

Copy link
Collaborator

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?

},

ObjectPattern: function(node) {
var propTypesNode = node.properties.find(function(property) {
Copy link
Member

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');
Copy link
Member

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({
Copy link
Member

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'
}],
Copy link
Collaborator

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

Copy link
Member

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)

Copy link
Member

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?

Copy link
Collaborator

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.

@oliviertassinari
Copy link
Contributor

oliviertassinari commented Feb 9, 2017

@iancmyers Thanks for working on that!
I have tried an iteration at the babel plugin with no luck.

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>
    );
  }
}

@ljharb
Copy link
Member

ljharb commented Feb 9, 2017

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 CustomView.propTypes = …, it shouldn't be difficult.

@iancmyers iancmyers force-pushed the icm-forbid-foreign-prop-type branch 2 times, most recently from 66e8e24 to 6241f4b Compare February 14, 2017 18:59
@iancmyers
Copy link
Contributor Author

@lencioni @ljharb I've taken another pass at this and addressed much of your feedback.

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
Copy link
Member

@ljharb ljharb left a 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;
Copy link
Member

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?

Copy link
Contributor Author

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.

@lencioni
Copy link
Collaborator

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants