Skip to content

Commit 690832f

Browse files
authored
Merge pull request #1209 from jseminck/rule-no-static-typos
Add rule no static typos
2 parents a5dc9b8 + afd6ea0 commit 690832f

File tree

6 files changed

+514
-5
lines changed

6 files changed

+514
-5
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#
102102
* [react/no-redundant-should-component-update](docs/rules/no-redundant-should-component-update.md): Prevent usage of `shouldComponentUpdate` when extending React.PureComponent
103103
* [react/no-render-return-value](docs/rules/no-render-return-value.md): Prevent usage of the return value of `React.render`
104104
* [react/no-set-state](docs/rules/no-set-state.md): Prevent usage of `setState`
105+
* [react/no-typos](docs/rules/no-typos.md): Prevent common casing typos
105106
* [react/no-string-refs](docs/rules/no-string-refs.md): Prevent using string references in `ref` attribute.
106107
* [react/no-unescaped-entities](docs/rules/no-unescaped-entities.md): Prevent invalid characters from appearing in markup
107108
* [react/no-unknown-property](docs/rules/no-unknown-property.md): Prevent usage of unknown DOM property (fixable)

docs/rules/no-typos.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Prevents common casing typos (react/no-typos)
2+
3+
Ensure no casing typos were made declaring static class properties
4+
5+
## Rule Details
6+
7+
This rule checks whether the declared static class properties related to React components
8+
do not contain any typos. It currently makes sure that the following class properties have
9+
no casing typos:
10+
11+
* propTypes
12+
* contextTypes
13+
* childContextTypes
14+
* defaultProps
15+
16+
The following patterns are considered warnings:
17+
18+
```js
19+
class MyComponent extends React.Component {
20+
static PropTypes = {}
21+
}
22+
23+
class MyComponent extends React.Component {
24+
static proptypes = {}
25+
}
26+
27+
class MyComponent extends React.Component {
28+
static ContextTypes = {}
29+
}
30+
31+
class MyComponent extends React.Component {
32+
static contexttypes = {}
33+
}
34+
35+
class MyComponent extends React.Component {
36+
static ChildContextTypes = {}
37+
}
38+
39+
class MyComponent extends React.Component {
40+
static childcontexttypes = {}
41+
}
42+
43+
class MyComponent extends React.Component {
44+
static DefaultProps = {}
45+
}
46+
47+
class MyComponent extends React.Component {
48+
static defaultprops = {}
49+
}
50+
```
51+
52+
The following patterns are not considered warnings:
53+
54+
```js
55+
class MyComponent extends React.Component {
56+
static propTypes = {}
57+
}
58+
59+
class MyComponent extends React.Component {
60+
static contextTypes = {}
61+
}
62+
63+
class MyComponent extends React.Component {
64+
static childContextTypes = {}
65+
}
66+
67+
class MyComponent extends React.Component {
68+
static defaultProps = {}
69+
}
70+
```

index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ const allRules = {
6565
'void-dom-elements-no-children': require('./lib/rules/void-dom-elements-no-children'),
6666
'jsx-tag-spacing': require('./lib/rules/jsx-tag-spacing'),
6767
'no-redundant-should-component-update': require('./lib/rules/no-redundant-should-component-update'),
68-
'boolean-prop-naming': require('./lib/rules/boolean-prop-naming')
68+
'boolean-prop-naming': require('./lib/rules/boolean-prop-naming'),
69+
'no-typos': require('./lib/rules/no-typos')
6970
};
7071

7172
function filterRules(rules, predicate) {

lib/rules/no-typos.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* @fileoverview Prevent common casing typos
3+
*/
4+
'use strict';
5+
6+
const Components = require('../util/Components');
7+
8+
// ------------------------------------------------------------------------------
9+
// Rule Definition
10+
// ------------------------------------------------------------------------------
11+
12+
const STATIC_CLASS_PROPERTIES = ['propTypes', 'contextTypes', 'childContextTypes', 'defaultProps'];
13+
14+
module.exports = {
15+
meta: {
16+
docs: {
17+
description: 'Prevent common casing typos',
18+
category: 'Stylistic Issues',
19+
recommended: false
20+
},
21+
schema: []
22+
},
23+
24+
create: Components.detect(function(context, components, utils) {
25+
function reportErrorIfCasingTypo(node, propertyName) {
26+
STATIC_CLASS_PROPERTIES.forEach(function(CLASS_PROP) {
27+
if (propertyName && CLASS_PROP.toLowerCase() === propertyName.toLowerCase() && CLASS_PROP !== propertyName) {
28+
context.report({
29+
node: node,
30+
message: 'Typo in static class property declaration'
31+
});
32+
}
33+
});
34+
}
35+
36+
return {
37+
ClassProperty: function(node) {
38+
if (!node.static || !utils.isES6Component(node.parent.parent)) {
39+
return;
40+
}
41+
42+
const tokens = context.getFirstTokens(node, 2);
43+
const propertyName = tokens[1].value;
44+
reportErrorIfCasingTypo(node, propertyName);
45+
},
46+
47+
MemberExpression: function(node) {
48+
const relatedComponent = utils.getRelatedComponent(node);
49+
50+
if (
51+
relatedComponent &&
52+
(utils.isES6Component(relatedComponent.node) || utils.isReturningJSX(relatedComponent.node))
53+
) {
54+
const propertyName = node.property.name;
55+
reportErrorIfCasingTypo(node, propertyName);
56+
}
57+
}
58+
};
59+
})
60+
};

lib/util/Components.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -319,13 +319,19 @@ function componentRule(rule, context) {
319319
* @param {ASTNode} ASTnode The AST node being checked
320320
*/
321321
findReturnStatement: function(node) {
322-
if (!node.value || !node.value.body || !node.value.body.body) {
322+
if (
323+
(!node.value || !node.value.body || !node.value.body.body) &&
324+
(!node.body || !node.body.body)
325+
) {
323326
return false;
324327
}
325-
let i = node.value.body.body.length - 1;
328+
329+
const bodyNodes = (node.value ? node.value.body.body : node.body.body);
330+
331+
let i = bodyNodes.length - 1;
326332
for (; i >= 0; i--) {
327-
if (node.value.body.body[i].type === 'ReturnStatement') {
328-
return node.value.body.body[i];
333+
if (bodyNodes[i].type === 'ReturnStatement') {
334+
return bodyNodes[i];
329335
}
330336
}
331337
return false;

0 commit comments

Comments
 (0)