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 rule no static typos #1209

Merged
merged 22 commits into from Jul 1, 2017
Merged

Conversation

jseminck
Copy link
Contributor

@jseminck jseminck commented May 19, 2017

My attempt to add this rule. It seemed pretty basic... so here it is! I am open to new ideas and improvements to the rule. Please let me know!

Issue: #1189

Currently it only checks forstatic class properties. And only for defaultProps, contextTypes and propTypes. It should be trivial to add new properties as well though.

Because it only checks for static properties, it means that having PropTypes as a non-static class property or a method definition is still allowed. Although honestly I do not know why anyone would do that? Perhaps the rule can be extended to also warn in case propTypes is not declared as a static class property?

The rule only works for React Components. Within classes that are not React components, anything is still possible.

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.

Thanks, this looks great!

@@ -0,0 +1,59 @@
# Prevent casing typos when declaring static propTypes, contextType and defaultProps
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add (react/no-static-typos) on this line.


Ensure no casing typos were made declaring static class properties

**Fixable:** This rule is automatically fixable using the `--fix` flag on the command line.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be fixable - it's not safe to do so.

It's a great rule to prevent people doing this by accident - but autofixing these can cause actual behavior changes in production. eslint core's policy is to never allow autofixes that could break code.

Copy link
Contributor Author

@jseminck jseminck May 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I will remove it.

I was actually not aware of this policy. So only stylistic issues should be fixable? Adding removing lines, spaces, semi-colons?

FWIW, The actual help for --fix actually says the following: This option instructs ESLint to try to fix as many issues as possible.

Considering the words "as many issues as possible" - I thought that every rule which we know how to fix should be auto fixable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not that it has to just be stylistic - but generally eslint errs on the side of caution to avoid breaking code.

If this rule warns, it'll only be for two reasons: 1) a typo, which is totally autofixable - and 2) a badly but intentionally chosen name, and the user needs to know to manually rename it.


* propTypes
* contextTypes
* defaultProps
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about childContextTypes?

@jseminck
Copy link
Contributor Author

jseminck commented May 23, 2017

Tackled your comments. I was not aware of childContextTypes - it is not something I have had to use before. If there are any properties that should be added please let me know. It's pretty trivial to add them - we just have to make sure the test cases are added as well.

Also I wanted to ask: as I was working on this, I thought that it might be worth it to detect small spelling mistakes as well. We should be able to easily detect 1-letter spelling mistakes. But I am not sure how far this plugin wants to go?

@ljharb
Copy link
Member

ljharb commented May 23, 2017

Detecting spelling mistakes seems like a slippery slope.

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.

For each test case (that are currently all using babel-eslint), can you please add a duplicate test case that uses the default parser?

@jseminck jseminck changed the title Add rule no static typos (including fixer) Add rule no static typos May 31, 2017
@jseminck
Copy link
Contributor Author

jseminck commented May 31, 2017

The default parser throws an error. I assume that is the case because the class properties proposal is still stage-0 and it is not supported by espree (which is the default parser).

This is the error that the test then gives: https://astexplorer.net/#/gist/da19b693a7a3d48bcc3fb249180b5295/a5875bd2c21133f64ce9f397ff82adb5e37da26a

Please correct me if I am wrong, would need guidance what exactly needs to be changed. :)

Edit: Did you mean that the following should also be validated? Because currently this is not checked and does not throw a warning.

class Hello extends Component [}
Hello.pRopTyPeS = { ... } // should throw an error because of casing issues?

I did remove the require("babel-eslint"); at the top of the test file. That seems like it was not needed.

@ljharb
Copy link
Member

ljharb commented May 31, 2017

Indeed, I'm talking about assigning it outside the class - this should apply to the currently-standard Foo.propTypes = {} as well as the not-yet-standard class properties form.

@jseminck
Copy link
Contributor Author

jseminck commented Jun 5, 2017

I added tests and support for assignment outside the class.

But I cannot get it to work when assigning on a stateless function component: I am not sure how to detect whether the function is a stateless function component or just some random function yet.

      function MyComponent(props) {
        return <div>{props.name}</div>;
      }

      QuestionList.PrOpTyPeS = {
        name: PropTypes.string.isRequired,
      };

@ljharb
Copy link
Member

ljharb commented Jun 5, 2017

We have Components.detect used elsewhere in the project which should be able to handle that for you.

@jseminck
Copy link
Contributor Author

I spent quite a bit of time on this but no luck so far. I cannot figure out how to properly detect a stateless function component so that the following becomes invalid:

function MyComponent() { return (<div>{this.props.myprop}</div>) };
MyComponent.pRoPTyPeS = { myProp: PropTypes.string }

But the following is valid (since it is not a stateless component):

function SomeRandomFunction() { return {} };
MyComponent.pRoPTyPeS = { }

I tried using components.isReturningJSX but didn't get anywhere.

I added all the test cases, maybe someone else has any idea?

@ljharb
Copy link
Member

ljharb commented Jun 13, 2017

Components.detect?

@jseminck
Copy link
Contributor Author

So I finally managed to fix this. I also rebased everything unto the latest master.

I ended up extending Components.findReturnStatement to also work when passing in FunctionDeclaration node. Previously, it would not work for this specific node.

This way, I could now use utils.isReturningJSX to properly detect a functional stateless component compared to a normal function..

I tried various other approaches from the existing Components API but nothing wold make the tests pass as I expected them to.

README.md Outdated
@@ -100,6 +100,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#
* [react/no-redundant-should-component-update](docs/rules/no-redundant-should-component-update.md): Prevent usage of `shouldComponentUpdate` when extending React.PureComponent
* [react/no-render-return-value](docs/rules/no-render-return-value.md): Prevent usage of the return value of `React.render`
* [react/no-set-state](docs/rules/no-set-state.md): Prevent usage of `setState`
* [react/no-static-typos](docs/rules/no-static-typos.md): Prevent casing typos when declaring static class properties.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to casing, it would be nice to flag also anything with a small levenshtein distance. That could be done as a separate PR.

And, if you are interested, it would be really cool to have a similar rule for detecting typos in lifecycle methods: #843

I wonder if this rule should have a more general name like react/no-typos and make it work also for lifecycle methods?

Copy link
Contributor Author

@jseminck jseminck Jun 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggested the spelling error detection also earlier in the PR: #1209 (comment) and reply: #1209 (comment)

I will look into the similar issue regarding lifecycle methods. Would be useful to have it there as well, as I never remember if it was prevProps, nextProps, prevState or nextState (which one and in which order).

I think the rename would make sense, if we add support for lifecycle methods... which I think, should be straight forward. (though I though that initially about this rule as well and it turned out to take a bit of time :D)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as I never remember if it was prevProps, nextProps, prevState or nextState

To be clear, my suggestion is to apply this rule to lifecycle method names, not their arguments. e.g. componentWillMount. A rule that enforces naming of lifecycle method arguments would be interesting, but I think that is definitely a different rule than typo detection.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if we're planning on extending it in the future but not yet, then we should rename the rule to be more generic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I will rename to no-typos and update the docs accordingly.

We can then add casing typos for lifecycle methods in another PR. And we can look into detecting small Levenshtein distances in a separate PR as well.

Sounds good?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds great!

if (CLASS_PROP.toLowerCase() === propertyName.toLowerCase() && CLASS_PROP !== propertyName) {
context.report({
node: node,
message: 'Typo in static class property declaration'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like this might be pretty straightforward to write a fixer for. Would you be interested in adding that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think a fixer is safe here; there's no way to know if it's intentional or not.

Joachim Seminck added 13 commits June 28, 2017 09:29
Fix an error with closing the code tag
Add "fixable" to the documentation
Add link to the main README
Added the rule name

Decided to not mention all the possible properties that we check for in the title, as adding childContextTypes in the
next commit may end up making this title too long.
findReturnStatement expect "node.value.body.body" to exist, but for FunctionDeclarations
the correct path to the ReturnStatement is "node.body.body"
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.

We should also add tests for computed static property names (to ensure they're not reported).

@jseminck
Copy link
Contributor Author

Could you please elaborate a bit more what you mean exactly?

I added a test case for this, as this was currently causing a crash:

      class First extends React.Component {}
      First["prop" + "Types"] = {};'
      First["context" + "Types"] = {};'
      First["childContext" + "Types"] = {};'
      First["default" + "Props"] = {};

And yeah - this is not supported. E.g. First["deFauLT" + "prOps"] = {} will not error.

@ljharb
Copy link
Member

ljharb commented Jun 29, 2017

I expect, from the implementation, that computed property names are ignored. I'm asking for tests to ensure they are ignored - to prevent them accidentally being considered in the future, and to ensure that considering them is a breaking change.

@jseminck
Copy link
Contributor Author

jseminck commented Jul 1, 2017

I understood why we want the test cases. I just wasn't 100% sure that I understood correctly what you meant with "computed static property names".

I understood this was used for:

const myObj = {
  ["myKey-" + getIndex()]: "myValue"
}

But I don't see how this syntax can be used to declare these properties. The tests I added are basically what this would transpile to:

const myObj = {}
myObj["myKey-" + getIndex()]: "myValue

I hope that's all that is needed to finalize this, as I'd like to start focussing on the next steps. :)

@ljharb ljharb merged commit 690832f into jsx-eslint:master Jul 1, 2017
@ljharb ljharb mentioned this pull request Jul 17, 2017
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

None yet

3 participants