Skip to content

Commit 8b7b848

Browse files
committed
Fix props detection when used in an object (fixes #41)
1 parent 2d9004b commit 8b7b848

File tree

2 files changed

+46
-15
lines changed

2 files changed

+46
-15
lines changed

lib/util/component.js

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,29 +36,42 @@ function isReactComponent(context, node) {
3636
return Boolean(isEmptyComponentRender || isComponentRender);
3737
}
3838

39+
/**
40+
* Detect if the node is a component definition
41+
* @param {ASTNode} node The AST node being checked.
42+
* @returns {Boolean} True the node is a component definition, false if not.
43+
*/
44+
function isComponentDefinition(node) {
45+
var isES6Component = node.type === 'ClassDeclaration';
46+
var isES5Component = Boolean(
47+
node.type === 'ObjectExpression' &&
48+
node.parent &&
49+
node.parent.callee &&
50+
node.parent.callee.object &&
51+
node.parent.callee.property &&
52+
node.parent.callee.object.name === 'React' &&
53+
node.parent.callee.property.name === 'createClass'
54+
);
55+
return isES5Component || isES6Component;
56+
}
57+
3958
/**
4059
* Get the React component ASTNode from any child ASTNode
4160
* @param {Object} context The current rule context.
4261
* @param {ASTNode} node The AST node being checked.
4362
* @returns {ASTNode} The ASTNode of the React component.
4463
*/
4564
function getNode(context, node) {
46-
var componentNode;
47-
48-
if (node.type === 'ClassDeclaration' || node.type === 'ObjectExpression') {
49-
componentNode = node;
50-
} else {
51-
var ancestors = context.getAncestors().reverse();
52-
for (var i = 0, j = ancestors.length; i < j; i++) {
53-
if (ancestors[i].type === 'ClassDeclaration' || ancestors[i].type === 'ObjectExpression') {
54-
componentNode = ancestors[i];
55-
break;
56-
}
57-
}
58-
}
65+
var componentNode = null;
66+
var ancestors = context.getAncestors().reverse();
5967

60-
if (!componentNode) {
61-
return null;
68+
ancestors.unshift(node);
69+
70+
for (var i = 0, j = ancestors.length; i < j; i++) {
71+
if (isComponentDefinition(ancestors[i])) {
72+
componentNode = ancestors[i];
73+
break;
74+
}
6275
}
6376

6477
return componentNode;
@@ -188,6 +201,7 @@ module.exports = {
188201
DEFAULT_COMPONENT_NAME: DEFAULT_COMPONENT_NAME,
189202
isReactComponent: isReactComponent,
190203
getNode: getNode,
204+
isComponentDefinition: isComponentDefinition,
191205
getIdentifiers: getIdentifiers,
192206
List: List
193207
};

tests/lib/rules/prop-types.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,23 @@ eslintTester.addRuleTest('lib/rules/prop-types', {
136136
classes: true,
137137
jsx: true
138138
}
139+
}, {
140+
code: [
141+
'var Hello = React.createClass({',
142+
' propTypes: {',
143+
' name: React.PropTypes.object.isRequired',
144+
' },',
145+
' render: function() {',
146+
' var user = {',
147+
' name: this.props.name',
148+
' };',
149+
' return <div>Hello {user.name}</div>;',
150+
' }',
151+
'});'
152+
].join('\n'),
153+
ecmaFeatures: {
154+
jsx: true
155+
}
139156
}
140157
],
141158

0 commit comments

Comments
 (0)