Skip to content

Commit

Permalink
Fix prop-types for namespaced components (fixes #621)
Browse files Browse the repository at this point in the history
  • Loading branch information
yannickcr committed Jun 19, 2016
1 parent 16e7019 commit 6168ac5
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 9 deletions.
45 changes: 36 additions & 9 deletions lib/util/Components.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ function componentRule(rule, context) {
var j;
var k;
var l;
var componentName;
var componentNode;
// Get the component path
var componentPath = [];
while (node) {
Expand All @@ -344,6 +346,7 @@ function componentRule(rule, context) {
node = node.object;
}
componentPath.reverse();
componentName = componentPath.slice(0, componentPath.length - 1).join('.');

// Find the variable in the current scope
var variableName = componentPath.shift();
Expand All @@ -362,7 +365,31 @@ function componentRule(rule, context) {
return null;
}

// Find the variable declaration
// Try to find the component using variable references
var refs = variableInScope.references;
var refId;
for (i = 0, j = refs.length; i < j; i++) {
refId = refs[i].identifier;
if (refId.parent && refId.parent.type === 'MemberExpression') {
refId = refId.parent;
}
if (sourceCode.getText(refId) !== componentName) {
continue;
}
if (refId.type === 'MemberExpression') {
componentNode = refId.parent.right;
} else if (refId.parent && refId.parent.type === 'VariableDeclarator') {
componentNode = refId.parent.init;
}
break;
}

if (componentNode) {
// Return the component
return components.add(componentNode, 1);
}

// Try to find the component using variable declarations
var defInScope;
var defs = variableInScope.defs;
for (i = 0, j = defs.length; i < j; i++) {
Expand All @@ -374,27 +401,27 @@ function componentRule(rule, context) {
if (!defInScope || !defInScope.node) {
return null;
}
node = defInScope.node.init || defInScope.node;
componentNode = defInScope.node.init || defInScope.node;

// Traverse the node properties to the component declaration
for (i = 0, j = componentPath.length; i < j; i++) {
if (!node.properties) {
if (!componentNode.properties) {
continue;
}
for (k = 0, l = node.properties.length; k < l; k++) {
if (node.properties[k].key.name === componentPath[i]) {
node = node.properties[k];
for (k = 0, l = componentNode.properties.length; k < l; k++) {
if (componentNode.properties[k].key.name === componentPath[i]) {
componentNode = componentNode.properties[k];
break;
}
}
if (!node || !node.value) {
if (!componentNode || !componentNode.value) {
return null;
}
node = node.value;
componentNode = componentNode.value;
}

// Return the component
return components.add(node, 1);
return components.add(componentNode, 1);
}
};

Expand Down
67 changes: 67 additions & 0 deletions tests/lib/rules/prop-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,45 @@ ruleTester.run('prop-types', rule, {
'}'
].join('\n'),
parserOptions: parserOptions
}, {
code: [
'let Greetings = class extends React.Component {',
' render () {',
' return <div>Hello {this.props.name}</div>;',
' }',
'};',
'Greetings.propTypes = {',
' name: React.PropTypes.string',
'}'
].join('\n'),
parserOptions: parserOptions
}, {
code: [
'let Greetings = {',
' Hello: class extends React.Component {',
' render () {',
' return <div>Hello {this.props.name}</div>;',
' }',
' }',
'}',
'Greetings.Hello.propTypes = {',
' name: React.PropTypes.string',
'};'
].join('\n'),
parserOptions: parserOptions
}, {
code: [
'let Greetings = {};',
'Greetings.Hello = class extends React.Component {',
' render () {',
' return <div>Hello {this.props.name}</div>;',
' }',
'};',
'Greetings.Hello.propTypes = {',
' name: React.PropTypes.string',
'}'
].join('\n'),
parserOptions: parserOptions
}
],

Expand Down Expand Up @@ -2052,6 +2091,34 @@ ruleTester.run('prop-types', rule, {
errors: [
{message: '\'result.notok\' is missing in props validation'}
]
}, {
code: [
'let Greetings = class extends React.Component {',
' render () {',
' return <div>Hello {this.props.name}</div>;',
' }',
'}',
'Greetings.propTypes = {};'
].join('\n'),
parserOptions: parserOptions,
errors: [{
message: '\'name\' is missing in props validation'
}]
}, {
code: [
'let Greetings = {',
' Hello: class extends React.Component {',
' render () {',
' return <div>Hello {this.props.name}</div>;',
' }',
' }',
'}',
'Greetings.Hello.propTypes = {};'
].join('\n'),
parserOptions: parserOptions,
errors: [{
message: '\'name\' is missing in props validation'
}]
}, {
code: [
'let Greetings = {};',
Expand Down

0 comments on commit 6168ac5

Please sign in to comment.