Skip to content

Commit

Permalink
add support for arrow functions
Browse files Browse the repository at this point in the history
  • Loading branch information
ganimomer committed Sep 6, 2015
1 parent c36ca65 commit 245bcff
Show file tree
Hide file tree
Showing 11 changed files with 81 additions and 24 deletions.
3 changes: 1 addition & 2 deletions lib/rules/matches-prop-shorthand.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ module.exports = function (context) {
var SUPPORT_MATCHES = ['find', 'detect', 'filter', 'select', 'reject'];

function shouldPreferMatches(func) {
var firstLine = astUtil.getFirstFunctionLine(func);
return astUtil.isReturnStatement(firstLine) && astUtil.isEqEqEqToParamMember(firstLine.argument, astUtil.getFirstParamName(func));
return astUtil.isEqEqEqToParamMember(astUtil.getValueReturnedInFirstLine(func), astUtil.getFirstParamName(func));
}

function methodSupportsShorthand(node) {
Expand Down
9 changes: 4 additions & 5 deletions lib/rules/prefer-compact.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,15 @@ module.exports = function (context) {
}

function isCallToBooleanCastOfParam(exp, paramName) {
return exp.type === 'CallExpression' && exp.callee.name === 'Boolean' && astUtil.isIdentifierOfParam(exp.arguments[0], paramName);
return exp && exp.type === 'CallExpression' && exp.callee.name === 'Boolean' && astUtil.isIdentifierOfParam(exp.arguments[0], paramName);
}

function isBooleanCastingFunction(func) {
var firstLine = astUtil.getFirstFunctionLine(func);
var returnValue = astUtil.getValueReturnedInFirstLine(func);
var paramName = astUtil.getFirstParamName(func);
return (func && func.type === 'Identifier' && func.name === 'Boolean') ||
(astUtil.isReturnStatement(firstLine) && (astUtil.isIdentifierOfParam(firstLine.argument, paramName) ||
isDoubleNegationOfParam(firstLine.argument, paramName) || isCallToBooleanCastOfParam(firstLine.argument, paramName)));

(astUtil.isIdentifierOfParam(returnValue, paramName) ||
isDoubleNegationOfParam(returnValue, paramName) || isCallToBooleanCastOfParam(returnValue, paramName));
}

return {
Expand Down
7 changes: 6 additions & 1 deletion lib/rules/prefer-filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
module.exports = function (context) {
var lodashUtil = require('../util/lodashUtil');
var astUtil = require('../util/astUtil');
var aliases = require('../util/aliases');

function isForEach(node) {
return aliases.isAliasOfMethod('forEach', astUtil.getMethodName(node));
}

function isIfWithoutElse(statement) {
return statement && statement.type === 'IfStatement' && !statement.alternate;
Expand All @@ -27,7 +32,7 @@ module.exports = function (context) {

return {
CallExpression: function (node) {
if (lodashUtil.isCallToMethod(node, 'forEach') && onlyHasSimplifiableIf(lodashUtil.getLodashIteratee(node))) {
if (isForEach(node) && onlyHasSimplifiableIf(lodashUtil.getLodashIteratee(node))) {
context.report(node, 'Prefer _.filter or _.some over an if statement inside a _.forEach');
}
}
Expand Down
8 changes: 3 additions & 5 deletions lib/rules/prefer-reject.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,17 @@ module.exports = function (context) {
var astUtil = require('../util/astUtil');

function isNegativeExpressionFunction(func) {
var firstLine = astUtil.getFirstFunctionLine(func);
var returnValue = astUtil.getValueReturnedInFirstLine(func);
var firstParamName = astUtil.getFirstParamName(func);
return astUtil.isReturnStatement(firstLine) &&
(astUtil.isNegationOfParamMember(firstLine.argument, firstParamName) ||
astUtil.isNotEqEqToParamMember(firstLine.argument, firstParamName));
return astUtil.isNegationOfParamMember(returnValue, firstParamName) ||
astUtil.isNotEqEqToParamMember(returnValue, firstParamName);
}

return {
CallExpression: function (node) {
if (lodashUtil.isCallToMethod(node, 'filter') && isNegativeExpressionFunction(lodashUtil.getLodashIteratee(node))) {
context.report(node, 'Prefer _.reject over negative condition');
}

}
};
};
3 changes: 1 addition & 2 deletions lib/rules/prop-shorthand.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ module.exports = function (context) {
var astUtil = require('../util/astUtil');

function canUseShorthand(func) {
var firstLine = astUtil.getFirstFunctionLine(func);
return astUtil.isReturnStatement(firstLine) && astUtil.isMemberExpOfArg(firstLine.argument, astUtil.getFirstParamName(func));
return astUtil.isMemberExpOfArg(astUtil.getValueReturnedInFirstLine(func), astUtil.getFirstParamName(func));
}

function methodSupportsShorthand(node) {
Expand Down
43 changes: 36 additions & 7 deletions lib/util/astUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,20 @@ function isMethodCall(node) {
return node && node.type === 'CallExpression' && node.callee.type === 'MemberExpression';
}

function isFunctionDefinitionWithBlock(node) {
return (node.type === 'FunctionExpression' || (node.type === 'ArrowFunctionExpression' && node.body.type === 'BlockStatement'));
}

function getFirstFunctionLine(node) {
return node && node.type === 'FunctionExpression' && _.get(node, 'body.body[0]');
if (node) {
if (isFunctionDefinitionWithBlock(node)) {
return _.get(node, 'body.body[0]');
}
if (node.type === 'ArrowFunctionExpression') {
return node.body;
}
}
return null;
}

function isPropAccess(node) {
Expand Down Expand Up @@ -49,24 +61,40 @@ function isAnySideArgMemberExp(exp, paramName) {
}

function isEqEqEqToParamMember(exp, paramName) {
return exp.type === 'BinaryExpression' && exp.operator === '===' && isAnySideArgMemberExp(exp, paramName);
return exp && exp.type === 'BinaryExpression' && exp.operator === '===' && isAnySideArgMemberExp(exp, paramName);
}
function isNotEqEqToParamMember(exp, paramName) {
return exp.type === 'BinaryExpression' && exp.operator === '!==' && isAnySideArgMemberExp(exp, paramName);
return exp && exp.type === 'BinaryExpression' && exp.operator === '!==' && isAnySideArgMemberExp(exp, paramName);
}

function isNegationExpression(exp) {
return exp.type === 'UnaryExpression' && exp.operator === '!';
return exp && exp.type === 'UnaryExpression' && exp.operator === '!';
}

function isNegationOfParamMember(exp, firstParamName) {
return isNegationExpression(exp) && isMemberExpOfArg(exp.argument, firstParamName);
}

function isIdentifierOfParam(exp, paramName) {
return paramName && exp.type === 'Identifier' && exp.name === paramName;
return exp && paramName && exp.type === 'Identifier' && exp.name === paramName;
}

function getValueReturnedInFirstLine(func) {
var firstLine = getFirstFunctionLine(func);
if (func) {
if (isFunctionDefinitionWithBlock(func)) {
return isReturnStatement(firstLine) ? firstLine.argument : null;
}
if (func.type === 'ArrowFunctionExpression') {
return firstLine;
}
}
return null;
}

function isCallFromObject(node, objName) {
return node && node.type === 'CallExpression' && _.get(node, 'callee.object.name') === objName;
}

module.exports = {
getCaller: getCaller,
Expand All @@ -75,12 +103,13 @@ module.exports = {
getFirstFunctionLine: getFirstFunctionLine,
isMemberExpOfArg: isMemberExpOfArg,
getFirstParamName: getFirstParamName,
isReturnStatement: isReturnStatement,
hasOnlyOneStatement: hasOnlyOneStatement,
isObjectOfMethodCall: isObjectOfMethodCall,
isEqEqEqToParamMember: isEqEqEqToParamMember,
isNotEqEqToParamMember: isNotEqEqToParamMember,
isNegationOfParamMember: isNegationOfParamMember,
isIdentifierOfParam: isIdentifierOfParam,
isNegationExpression: isNegationExpression
isNegationExpression: isNegationExpression,
getValueReturnedInFirstLine: getValueReturnedInFirstLine,
isCallFromObject: isCallFromObject
};
2 changes: 1 addition & 1 deletion lib/util/lodashUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var aliasMap = require('./aliases');
var astUtil = require('./astUtil');

function isLodashCall(node) {
return node && node.type === 'CallExpression' && _.get(astUtil.getCaller(node), 'name') === '_';
return astUtil.isCallFromObject(node, '_');
}

function isChainable(node) {
Expand Down
14 changes: 13 additions & 1 deletion tests/lib/rules/matches-prop-shorthand.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var RuleTester = require('eslint').RuleTester;
// ------------------------------------------------------------------------------

var ruleTester = new RuleTester();
var errors = [{message: 'Prefer matches property syntax'}];
ruleTester.run('matches-prop-shorthand', rule, {
valid: [{
code: 'var isPublic = _.find([], function (i) { return x.id; });'
Expand All @@ -26,11 +27,22 @@ ruleTester.run('matches-prop-shorthand', rule, {
code: 'var isPublic = _.find([], function (i) { return i.id == 3; });'
}, {
code: 'var isPublic = _.find([], function (i) { return i[0] === 3; });'
}, {
code: 'var isPublic = _.find([], i => i[0] === 3);',
ecmaFeatures: {arrowFunctions: true}
}],

invalid: [{
code: 'var isPublic = _.find([], function (i) { return i.id === 3; });',
errors: [{message: 'Prefer matches property syntax'}]
errors: errors
}, {
code: 'var isPublic = _.find(arr, i => i.id === 3)',
ecmaFeatures: {arrowFunctions: true},
errors: errors
}, {
code: 'var isPublic = _.find(arr, (i) => {return i.id === 3})',
ecmaFeatures: {arrowFunctions: true},
errors: errors
//}, {
// TODO: handle this
// code: '_.find(arr, function(i){ return i.b.c === compId; });',
Expand Down
6 changes: 6 additions & 0 deletions tests/lib/rules/prefer-compact.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ var ruleError = {message: 'Prefer _.compact over filtering of Boolean casting'};
ruleTester.run('prefer-compact', rule, {
valid: [{
code: 'var x = _.filter(arr, function(x) {return f(x) || g(x)})'
}, {
code: 'var x = _.filter(arr, function(x) {var a = 1; return f(x, a);})'
}],
invalid: [{
code: '_(arr).map(f).filter(function(x) {return x})',
Expand All @@ -29,5 +31,9 @@ ruleTester.run('prefer-compact', rule, {
}, {
code: '_.filter(arr, function(x) {return Boolean(x) })',
errors: [ruleError]
}, {
code: '_.filter(arr, x => !!x)',
ecmaFeatures: {arrowFunctions: true},
errors: [ruleError]
}]
});
4 changes: 4 additions & 0 deletions tests/lib/rules/prefer-reject.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,9 @@ ruleTester.run('prefer-reject', rule, {
}, {
code: '_.filter(arr, function(x) {return !x.isSomething})',
errors: [ruleError]
}, {
code: '_.filter(arr, x => !x.isSomething)',
ecmaFeatures: {arrowFunctions: true},
errors: [ruleError]
}]
});
6 changes: 6 additions & 0 deletions tests/lib/rules/prop-shorthand.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ ruleTester.run('prop-shorthand', rule, {
code: 'var ids = _.map([], function (i) { return i[k]; });'
}, {
code: 'var r = _.map([], function() { return React.PropTypes.object; })'
}, {
code: 'var r = _.map([])'
}],
invalid: [{
code: 'var ids = _(users).map(function (i) { return i.id; });',
Expand All @@ -41,5 +43,9 @@ ruleTester.run('prop-shorthand', rule, {
}, {
code: 'var ids = _.map([], function (i) { return i["id"]; });',
errors: errors
}, {
code: 'var ids = _.map([], i => i.id);',
ecmaFeatures: {arrowFunctions: true},
errors: errors
}]
});

0 comments on commit 245bcff

Please sign in to comment.