Skip to content

Commit 398d577

Browse files
committed
Add initial logic and tests for basic use cases
1 parent 90d65e5 commit 398d577

File tree

5 files changed

+258
-1
lines changed

5 files changed

+258
-1
lines changed

lib/rules/jsx-no-invalid-props.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* @fileoverview Rule to flag use of an invalid prop type
3+
* @author Craig Bilner
4+
*/
5+
"use strict";
6+
7+
var utils = require('../utilities');
8+
9+
//------------------------------------------------------------------------------
10+
// Rule Definition
11+
//------------------------------------------------------------------------------
12+
13+
module.exports = function (context) {
14+
15+
var validProps = {
16+
array: 1,
17+
bool: 1,
18+
boolean: 2,
19+
func: 1,
20+
function: 2,
21+
number: 1,
22+
object: 1,
23+
obj: 2,
24+
string: 1,
25+
any: 1,
26+
arrayOf: 1,
27+
element: 1,
28+
instanceOf: 1,
29+
node: 1,
30+
objectOf: 1,
31+
oneOf: 1,
32+
oneOfType: 1,
33+
shape: 1
34+
};
35+
36+
function isExpectedReactPropSyntax(declaration) {
37+
return declaration.value.object && ((
38+
declaration.value.object.name === 'PropTypes'
39+
)
40+
|| (
41+
declaration.value.object.object.name === 'React'
42+
&& declaration.value.object.property.name === 'PropTypes'
43+
)
44+
|| (
45+
declaration.value.object.object.name === 'PropTypes'
46+
&& declaration.value.property.name === 'isRequired'
47+
)
48+
|| (
49+
declaration.value.object.object.object.name === 'React'
50+
&& declaration.value.object.object.property.name === 'PropTypes'
51+
&& declaration.value.property.name === 'isRequired'
52+
));
53+
}
54+
55+
function getPropValue(propName) {
56+
return validProps[propName];
57+
}
58+
59+
function checkProperties(declarations) {
60+
declarations.forEach(function (declaration) {
61+
if (isExpectedReactPropSyntax(declaration)) {
62+
var propName = '';
63+
var propValue = 0;
64+
if (declaration.value.property.name === 'isRequired') {
65+
propName = declaration.value.object.property.name;
66+
} else {
67+
propName = declaration.value.property.name;
68+
}
69+
70+
propValue = getPropValue(propName);
71+
72+
if (propValue !== 1) {
73+
context.report(declaration, propName + ' is not a valid PropType');
74+
}
75+
} else {
76+
context.report(declaration, 'unknown use of PropTypes');
77+
}
78+
});
79+
}
80+
81+
return {
82+
MemberExpression: function (node) {
83+
if (utils.isPropTypesDeclaration(node.property)) {
84+
var right = node.parent.right;
85+
if (right && right.type === 'ObjectExpression') {
86+
checkProperties(right.properties);
87+
}
88+
}
89+
}
90+
};
91+
92+
};
93+
94+
module.exports.schema = [
95+
// JSON Schema for rule options goes here
96+
];

lib/utilities.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
Borrowed from https://github.com/yannickcr/eslint-plugin-react
3+
*/
4+
5+
/**
6+
* Checks if node is `propTypes` declaration
7+
* @param {ASTNode} node The AST node being checked.
8+
* @returns {Boolean} True if node is `propTypes` declaration, false if not.
9+
*/
10+
function isPropTypesDeclaration(node) {
11+
12+
// Special case for class properties
13+
// (babel-eslint does not expose property name so we have to rely on tokens)
14+
if (node.type === 'ClassProperty') {
15+
var tokens = context.getFirstTokens(node, 2);
16+
if (tokens[0].value === 'propTypes' || tokens[1].value === 'propTypes') {
17+
return true;
18+
}
19+
return false;
20+
}
21+
22+
return Boolean(
23+
node &&
24+
node.name === 'propTypes'
25+
);
26+
}
27+
28+
module.exports = {
29+
isPropTypesDeclaration: isPropTypesDeclaration
30+
};

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
"author": "Craig",
1414
"main": "lib/index.js",
1515
"scripts": {
16-
"test": "mocha"
16+
"test": "node tests/lib/index.js"
1717
},
1818
"dependencies": {
19+
"babel-eslint": "^4.1.3",
1920
"requireindex": "~1.1.0"
2021
},
2122
"devDependencies": {

tests/lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
require('./rules/jsx-no-invalid-props');
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
'use strict';
2+
3+
require('babel-eslint');
4+
5+
var rule = require('../../../lib/rules/jsx-no-invalid-props');
6+
var RuleTester = require('eslint').RuleTester;
7+
var ruleTester = new RuleTester();
8+
9+
var ANY_ERROR_MESSAGE = 'Check your prop types';
10+
11+
ruleTester.run('jsx-no-invalid-props', rule, {
12+
valid: [
13+
{
14+
code: [
15+
'import React, {PropTypes} from "react";',
16+
'export default class TestComponent extends React.Component {',
17+
'constructor(props) {',
18+
'super(props);',
19+
'this.state = {};',
20+
'}',
21+
'render() {',
22+
'return (',
23+
'<div></div>',
24+
');',
25+
'}',
26+
'}',
27+
'TestComponent.propTypes = {',
28+
'a: PropTypes.array,',
29+
'b: PropTypes.bool,',
30+
'c: PropTypes.func,',
31+
'd: PropTypes.number,',
32+
'e: PropTypes.object,',
33+
'f: PropTypes.string,',
34+
'g: PropTypes.any,',
35+
'h: PropTypes.arrayOf,',
36+
'i: PropTypes.element,',
37+
'j: PropTypes.instanceOf,',
38+
'k: PropTypes.node,',
39+
'l: PropTypes.objectOf,',
40+
'm: PropTypes.oneOf,',
41+
'n: PropTypes.oneOfType,',
42+
'o: PropTypes.shape,',
43+
'p: PropTypes.array,',
44+
'q: PropTypes.bool,',
45+
'r: PropTypes.func,',
46+
's: PropTypes.number,',
47+
't: PropTypes.object,',
48+
'u: PropTypes.string,',
49+
'v: PropTypes.any,',
50+
'w: PropTypes.arrayOf,',
51+
'x: PropTypes.element,',
52+
'y: PropTypes.instanceOf,',
53+
'z: PropTypes.node,',
54+
'aa: PropTypes.objectOf,',
55+
'ab: PropTypes.oneOf,',
56+
'ac: PropTypes.oneOfType,',
57+
'ad: PropTypes.shape',
58+
'};'
59+
].join('\n'),
60+
ecmaFeatures: {
61+
jsx: true,
62+
modules: true,
63+
classes: true
64+
}
65+
}
66+
],
67+
invalid: [
68+
{
69+
code: [
70+
'import React, {PropTypes} from "react";',
71+
'export default class TestComponent extends React.Component {',
72+
'constructor(props) {',
73+
'super(props);',
74+
'this.state = {};',
75+
'}',
76+
'render() {',
77+
'return (',
78+
'<div></div>',
79+
');',
80+
'}',
81+
'}',
82+
'TestComponent.propTypes = {',
83+
'a: PropTypes.arr,',
84+
'};'
85+
].join('\n'),
86+
ecmaFeatures: {
87+
jsx: true,
88+
modules: true,
89+
classes: true
90+
},
91+
errors: [{
92+
message: 'arr is not a valid PropType',
93+
line: 14,
94+
column: 1,
95+
type: 'Property'
96+
}]
97+
},
98+
{
99+
code: [
100+
'import React, {PropTypes} from "react";',
101+
'export default class TestComponent extends React.Component {',
102+
'constructor(props) {',
103+
'super(props);',
104+
'this.state = {};',
105+
'}',
106+
'render() {',
107+
'return (',
108+
'<div></div>',
109+
');',
110+
'}',
111+
'}',
112+
'TestComponent.propTypes = {',
113+
'a: PropTypes,',
114+
'};'
115+
].join('\n'),
116+
ecmaFeatures: {
117+
jsx: true,
118+
modules: true,
119+
classes: true
120+
},
121+
errors: [{
122+
message: 'unknown use of PropTypes',
123+
line: 14,
124+
column: 1,
125+
type: 'Property'
126+
}]
127+
}
128+
]
129+
});

0 commit comments

Comments
 (0)