Skip to content
This repository has been archived by the owner on Mar 23, 2024. It is now read-only.

Commit

Permalink
requireMatchingFunctionName: requires function names to match member …
Browse files Browse the repository at this point in the history
…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
xaka committed Dec 23, 2014
1 parent 9aae17e commit a2e6501
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 0 deletions.
50 changes: 50 additions & 0 deletions README.md
Expand Up @@ -3697,6 +3697,56 @@ var a = 1;
[b].forEach(c);
```

### requireMatchingFunctionName

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. To maintain code readability we suggest
to prefix such function names with a dollar sign (`$`) i.e. `function $delete() { ... }`.

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() {}};
```

### ~~validateJSDoc~~

Please use the [JSCS-JSDoc](https://github.com/jscs-dev/jscs-jsdoc) plugin instead.
Expand Down
2 changes: 2 additions & 0 deletions lib/config/configuration.js
Expand Up @@ -595,6 +595,8 @@ Configuration.prototype.registerDefaultRules = function() {
this.registerRule(require('../rules/require-line-break-after-variable-assignment'));

this.registerRule(require('../rules/disallow-semicolons'));

this.registerRule(require('../rules/require-matching-function-name'));
};

/**
Expand Down
87 changes: 87 additions & 0 deletions lib/rules/require-matching-function-name.js
@@ -0,0 +1,87 @@
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) {
var checker;
switch (node.parentNode.type) {
// object.foo = function bar() {}
// object['foo'] = function bar() {}
case 'AssignmentExpression':
checker = checkForMember;
break;

// object = {foo: function bar() {}}
case 'Property':
checker = checkForProperty;
break;
}

if (checker) {
checker(node.parentNode, errors);
}
});
}
};

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);
}
72 changes: 72 additions & 0 deletions test/rules/require-matching-function-name.js
@@ -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);
}
});
});

0 comments on commit a2e6501

Please sign in to comment.