Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
marcgrr committed Oct 17, 2016
1 parent 00553b5 commit 7bfa7bf
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 84 deletions.
90 changes: 32 additions & 58 deletions lib/rules/no-unused-prop-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@
var Components = require('../util/Components');
var variable = require('../util/variable');

// ------------------------------------------------------------------------------
// Constants
// ------------------------------------------------------------------------------

var DIRECT_PROPS_REGEX = /^props\s*(\.|\[)/;

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
Expand Down Expand Up @@ -74,17 +68,15 @@ module.exports = {
}

/**
* Checks if we are using a prop
* Checks if this node is `this.props`.
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean} True if we are using a prop, false if not.
* @returns {Boolean} True if this node is `this.props`, false if not.
*/
function isPropTypesUsage(node) {
var isClassUsage = (
function isPropsMemberExpression(node) {
return (
(utils.getParentES6Component() || utils.getParentES5Component()) &&
node.object.type === 'ThisExpression' && node.property.name === 'props'
);
var isStatelessFunctionUsage = node.object.name === 'props';
return isClassUsage || isStatelessFunctionUsage;
}

/**
Expand Down Expand Up @@ -209,15 +201,26 @@ module.exports = {
}

/**
* Checks if a prop init name matches common naming patterns
* Checks if a node is an identifier referring to props
* @param {ASTNode} node The AST node being checked.
* @returns {Boolean} True if the prop name matches
*/
function isPropAttributeName (node) {
function isPropsIdentifier (node) {
return (
node.init.name === 'props' ||
node.init.name === 'nextProps' ||
node.init.name === 'prevProps'
node.type === 'Identifier' && (
node.parent.type === 'MemberExpression' ||
node.parent.type === 'VariableDeclarator'
) && (
(
(isInLifeCycleMethod(node) || utils.getParentStatelessComponent()) &&
node.name === 'props'
) || (
isInLifeCycleMethod(node) && (
node.name === 'nextProps' ||
node.name === 'prevProps'
)
)
)
);
}

Expand Down Expand Up @@ -483,41 +486,17 @@ module.exports = {
}
}

/**
* Check if we are in a class constructor
* @return {boolean} true if we are in a class constructor, false if not
*/
function inConstructor() {
var scope = context.getScope();
while (scope) {
if (scope.block && scope.block.parent && scope.block.parent.kind === 'constructor') {
return true;
}
scope = scope.upper;
}
return false;
}

/**
* Retrieve the name of a property node
* @param {ASTNode} node The AST node with the property.
* @return {string} the name of the property or undefined if not found
*/
function getPropertyName(node) {
var isDirectProp = DIRECT_PROPS_REGEX.test(sourceCode.getText(node));
var isInClassComponent = utils.getParentES6Component() || utils.getParentES5Component();
var isNotInConstructor = !inConstructor(node);
if (isDirectProp && isInClassComponent && isNotInConstructor) {
return void 0;
}
if (!isDirectProp) {
node = node.parent;
}
var property = node.property;
var property = node.parent.property;
if (property) {
switch (property.type) {
case 'Identifier':
if (node.computed) {
if (node.parent.computed) {
return '__COMPUTED_PROP__';
}
return property.name;
Expand All @@ -530,7 +509,7 @@ module.exports = {
}
// falls through
default:
if (node.computed) {
if (node.parent.computed) {
return '__COMPUTED_PROP__';
}
break;
Expand All @@ -550,6 +529,7 @@ module.exports = {
var allNames;
var properties;
switch (node.type) {
case 'Identifier':
case 'MemberExpression':
name = getPropertyName(node);
if (name) {
Expand Down Expand Up @@ -582,16 +562,9 @@ module.exports = {
(node.id.properties[i].key.name === 'props' || node.id.properties[i].key.value === 'props') &&
node.id.properties[i].value.type === 'ObjectPattern'
);
// let {firstname} = props
var genericDestructuring = isPropAttributeName(node) && (
utils.getParentStatelessComponent() ||
isInLifeCycleMethod(node)
);

if (thisDestructuring) {
properties = node.id.properties[i].value.properties;
} else if (genericDestructuring) {
properties = node.id.properties;
} else {
continue;
}
Expand Down Expand Up @@ -828,13 +801,8 @@ module.exports = {
var destructuring = node.init && node.id && node.id.type === 'ObjectPattern';
// let {props: {firstname}} = this
var thisDestructuring = destructuring && node.init.type === 'ThisExpression';
// let {firstname} = props
var statelessDestructuring = destructuring && isPropAttributeName(node) && (
utils.getParentStatelessComponent() ||
isInLifeCycleMethod(node)
);

if (!thisDestructuring && !statelessDestructuring) {
if (!thisDestructuring) {
return;
}
markPropTypesAsUsed(node);
Expand All @@ -848,7 +816,7 @@ module.exports = {

MemberExpression: function(node) {
var type;
if (isPropTypesUsage(node)) {
if (isPropsMemberExpression(node)) {
type = 'usage';
} else if (isPropTypesDeclaration(node.property)) {
type = 'declaration';
Expand All @@ -870,6 +838,12 @@ module.exports = {
}
},

Identifier: function(node) {
if (isPropsIdentifier(node)) {
markPropTypesAsUsed(node);
}
},

MethodDefinition: function(node) {
if (!isPropTypesDeclaration(node.key)) {
return;
Expand Down
8 changes: 4 additions & 4 deletions lib/util/Components.js
Original file line number Diff line number Diff line change
Expand Up @@ -347,12 +347,12 @@ function componentRule(rule, context) {
var isFunction = /Function/.test(node.type); // Functions
var isMethod = node.parent && node.parent.type === 'MethodDefinition'; // Classes methods
var isArgument = node.parent && node.parent.type === 'CallExpression'; // Arguments (callback, etc.)
// Stop moving up if we reach a class or an argument (like a callback)
if (isClass || isArgument) {
// Stop moving up if we reach a class
if (isClass) {
return null;
}
// Return the node if it is a function that is not a class method
if (isFunction && !isMethod) {
// Return the node if it is a function that is not a class method and not a callback argument
if (isFunction && !isMethod && !isArgument) {
return node;
}
scope = scope.upper;
Expand Down
Loading

0 comments on commit 7bfa7bf

Please sign in to comment.