This repository has been archived by the owner on Mar 23, 2024. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New rule: requireMatchingFunctionName
Requires function names to match member and property names Example of member name mismatch: ```js var test = {}; test.foo = function bar() {}; ``` Example of property name mismatch: ```js var test = {foo: function bar() {}}; ``` Fixes #846 Closes #850
- Loading branch information
Showing
3 changed files
with
205 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
/** | ||
* Requires function names to match member and property names. | ||
* | ||
* It doesn't affect anonymous functions nor functions assigned to members or | ||
* properties named with a reserved word. | ||
* | ||
* Type: `Boolean` | ||
* | ||
* Value: `true` | ||
* | ||
* #### Example | ||
* | ||
* ```js | ||
* "requireMatchingFunctionName": true | ||
* ``` | ||
* | ||
* ##### Valid | ||
* | ||
* ```js | ||
* var test = {}; | ||
* test.foo = function foo() {}; | ||
* ``` | ||
* | ||
* ```js | ||
* var test = {}; | ||
* test['foo'] = function foo() {}; | ||
* ``` | ||
* | ||
* ```js | ||
* var test = {foo: function foo() {}}; | ||
* ``` | ||
* | ||
* ##### Invalid | ||
* | ||
* ```js | ||
* var test = {}; | ||
* test.foo = function bar() {}; | ||
* ``` | ||
* | ||
* ```js | ||
* var test = {}; | ||
* test['foo'] = function bar() {}; | ||
* ``` | ||
* | ||
* ```js | ||
* var test = {foo: function bar() {}}; | ||
* ``` | ||
*/ | ||
|
||
var assert = require('assert'); | ||
var utils = require('../../lib/utils'); | ||
|
||
module.exports = function() {}; | ||
|
||
module.exports.prototype = { | ||
configure: function(requireMatchingFunctionName) { | ||
assert( | ||
requireMatchingFunctionName === true, | ||
'requireMatchingFunctionName option requires true value or should be removed' | ||
); | ||
}, | ||
|
||
getOptionName: function() { | ||
return 'requireMatchingFunctionName'; | ||
}, | ||
|
||
check: function(file, errors) { | ||
file.iterateNodesByType(['FunctionExpression'], function(node) { | ||
switch (node.parentNode.type) { | ||
// object.foo = function bar() {} | ||
// object['foo'] = function bar() {} | ||
case 'AssignmentExpression': | ||
checkForMember(node.parentNode, errors); | ||
break; | ||
|
||
// object = {foo: function bar() {}} | ||
case 'Property': | ||
checkForProperty(node.parentNode, errors); | ||
break; | ||
} | ||
}); | ||
} | ||
}; | ||
|
||
function checkForMember(assignment, errors) { | ||
// We don't care about anonymous functions as | ||
// those should be enforced by separate rule | ||
if (isAnonymousFunction(assignment.right)) { | ||
return; | ||
} | ||
|
||
// Relax a bit when reserved word is detected | ||
if (isReservedName(assignment.left.property.name)) { | ||
return; | ||
} | ||
|
||
if (assignment.left.property.name !== assignment.right.id.name) { | ||
errors.add( | ||
'Function name does not match member name', | ||
assignment.loc.start | ||
); | ||
} | ||
} | ||
|
||
function checkForProperty(property, errors) { | ||
// We don't care about anonymous functions as | ||
// those should be enforced by separate rule | ||
if (isAnonymousFunction(property.value)) { | ||
return; | ||
} | ||
|
||
// Relax a bit when reserved word is detected | ||
if (isReservedName(property.key.name)) { | ||
return; | ||
} | ||
|
||
if (property.key.name !== property.value.id.name) { | ||
errors.add( | ||
'Function name does not match property name', | ||
property.loc.start | ||
); | ||
} | ||
} | ||
|
||
function isAnonymousFunction(node) { | ||
return !node.id; | ||
} | ||
|
||
function isReservedName(name) { | ||
return utils.isEs3Keyword(name) || utils.isEs3FutureReservedWord(name); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
var Checker = require('../../lib/checker'); | ||
var assert = require('assert'); | ||
|
||
describe('rules/require-matching-function-name', function() { | ||
var checker; | ||
|
||
beforeEach(function() { | ||
checker = new Checker(); | ||
checker.registerDefaultRules(); | ||
}); | ||
|
||
describe('option value true', function() { | ||
beforeEach(function() { | ||
checker.configure({ requireMatchingFunctionName: true }); | ||
}); | ||
|
||
// TODO: | ||
// - assigning function to variable (both names should match) | ||
// - assigning function declaration to member | ||
// - assigning function declaration to property | ||
// For both cases we should track identifier back, check whether | ||
// it's a function and if so, compare the names | ||
|
||
it('should report function name mismatch when assigning to member', function() { | ||
assertErrorForMemberNameMismatch('var test = {}; test.foo = function bar() {};'); | ||
}); | ||
|
||
it('should report function name mismatch when assigning to member via ["..."]', function() { | ||
assertErrorForMemberNameMismatch('var test = {}; test["foo"] = function bar() {};'); | ||
}); | ||
|
||
it('should NOT report function name mismatch when assigning anonymous to member', function() { | ||
assertNoErrors('var test = {}; test.foo = function() {};'); | ||
}); | ||
|
||
it('should NOT report function name mismatch when member name is reserved word', function() { | ||
assertNoErrors('var test = {}; test.delete = function $delete() {};'); | ||
}); | ||
|
||
it('should report function name mismatch when assigning to property', function() { | ||
assertErrorForPropertyNameMismatch('var test = {foo: function bar() {}};'); | ||
}); | ||
|
||
it('should NOT report function name mismatch when assigning anonymous to property', function() { | ||
assertNoErrors('var test = {foo: function() {}};'); | ||
}); | ||
|
||
it('should NOT report function name mismatch when property name is reserved word', function() { | ||
assertNoErrors('var test = {delete: function $delete() {}};'); | ||
}); | ||
|
||
function assertErrorForMemberNameMismatch(js) { | ||
assertError(js, 'Function name does not match member name'); | ||
} | ||
|
||
function assertErrorForPropertyNameMismatch(js) { | ||
assertError(js, 'Function name does not match property name'); | ||
} | ||
|
||
function assertError(js, message) { | ||
var errors = checker.checkString(js).getErrorList(); | ||
assert(errors.length); | ||
assert.equal(errors[0].rule, 'requireMatchingFunctionName'); | ||
assert.equal(errors[0].message, message); | ||
} | ||
|
||
function assertNoErrors(js) { | ||
var errors = checker.checkString(js).getErrorList(); | ||
assert.equal(errors.length, 0); | ||
} | ||
}); | ||
}); |