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

Commit ec8c0b0

Browse files
Gerhutmarkelog
authored andcommitted
requireMatchingFunctionName: add includeModuleExports option
By default, assigning an unmatched named function to module.exports is ignored, unless "includeModuleExports": true is configured in this rule. Closes gh-1927
1 parent 7e8c2af commit ec8c0b0

2 files changed

Lines changed: 116 additions & 27 deletions

File tree

lib/rules/require-matching-function-name.js

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
* Requires function names to match member and property names.
33
*
44
* It doesn't affect anonymous functions nor functions assigned to members or
5-
* properties named with a reserved word.
5+
* properties named with a reserved word. Assigning to `module.exports` is also
6+
* ignored, unless `includeModuleExports: true` is configured.
67
*
7-
* Type: `Boolean`
8+
* Types: `Boolean` or `Object`
89
*
9-
* Value: `true`
10+
* Values: `true` or Object with `includeModuleExports: true`
1011
*
1112
* #### Example
1213
*
@@ -30,6 +31,14 @@
3031
* var test = {foo: function foo() {}};
3132
* ```
3233
*
34+
* ```js
35+
* module.exports = function foo() {};
36+
* ```
37+
*
38+
* ```js
39+
* module['exports'] = function foo() {};
40+
* ```
41+
*
3342
* ##### Invalid
3443
*
3544
* ```js
@@ -45,6 +54,27 @@
4554
* ```js
4655
* var test = {foo: function bar() {}};
4756
* ```
57+
*
58+
* ```js
59+
* var test = {module: {}};
60+
* test.module.exports = function foo() {};
61+
* ```
62+
*
63+
* #### Example
64+
*
65+
* ```js
66+
* "requireMatchingFunctionName": { "includeModuleExports": true }
67+
* ```
68+
*
69+
* ##### Invalid
70+
*
71+
* ```js
72+
* module.exports = function foo() {};
73+
* ```
74+
*
75+
* ```js
76+
* module['exports'] = function foo() {};
77+
* ```
4878
*/
4979

5080
var assert = require('assert');
@@ -54,24 +84,33 @@ module.exports = function() {};
5484

5585
module.exports.prototype = {
5686
configure: function(requireMatchingFunctionName) {
57-
assert(
58-
requireMatchingFunctionName === true,
59-
'requireMatchingFunctionName option requires true value or should be removed'
60-
);
87+
if (typeof requireMatchingFunctionName === 'object') {
88+
assert(requireMatchingFunctionName.includeModuleExports === true,
89+
'requireMatchingFunctionName option requires includeModuleExports property to be true for object');
90+
this._includeModuleExports = requireMatchingFunctionName.includeModuleExports;
91+
} else {
92+
assert(
93+
requireMatchingFunctionName === true,
94+
'requireMatchingFunctionName option requires true value or should be removed'
95+
);
96+
}
6197
},
6298

6399
getOptionName: function() {
64100
return 'requireMatchingFunctionName';
65101
},
66102

67103
check: function(file, errors) {
104+
var _includeModuleExports = this._includeModuleExports;
68105
file.iterateNodesByType(['FunctionExpression'], function(node) {
69106
switch (node.parentNode.type) {
70107
// var foo = function bar() {}
71108
// object.foo = function bar() {}
72109
// object['foo'] = function bar() {}
73110
case 'AssignmentExpression':
74-
checkForMember(node.parentNode, skip, errors);
111+
if (_includeModuleExports || !_isModuleExports(node.parentNode.left)) {
112+
checkForMember(node.parentNode, skip, errors);
113+
}
75114
break;
76115

77116
// object = {foo: function bar() {}}
@@ -96,6 +135,26 @@ module.exports.prototype = {
96135
}
97136
};
98137

138+
function _isModuleExports(pattern) {
139+
if (pattern.type === 'MemberExpression') {
140+
// must be module.sth
141+
if (pattern.object.type === 'Identifier' &&
142+
pattern.object.name === 'module') {
143+
144+
if (pattern.property.type === 'Identifier' &&
145+
pattern.property.name === 'exports') {
146+
// sth.exports
147+
return true;
148+
} else if (pattern.property.type === 'Literal' &&
149+
pattern.property.value === 'exports') {
150+
// sth["exports"]
151+
return true;
152+
}
153+
}
154+
}
155+
return false;
156+
}
157+
99158
/**
100159
* Fetching name from a Pattern
101160
*

test/specs/rules/require-matching-function-name.js

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -89,24 +89,54 @@ describe('rules/require-matching-function-name', function() {
8989
assertNoErrors('let [ bar ] = [ function bar(){} ];');
9090
});
9191

92-
function assertErrorForMemberNameMismatch(js) {
93-
assertError(js, 'Function name does not match member name');
94-
}
95-
96-
function assertErrorForPropertyNameMismatch(js) {
97-
assertError(js, 'Function name does not match property name');
98-
}
99-
100-
function assertError(js, message) {
101-
var errors = checker.checkString(js).getErrorList();
102-
expect(errors.length).to.be.at.least(1);
103-
expect(errors[0].rule).to.equal('requireMatchingFunctionName');
104-
expect(errors[0].message).to.equal(message);
105-
}
106-
107-
function assertNoErrors(js) {
108-
var errors = checker.checkString(js).getErrorList();
109-
expect(errors.length).to.equal(0);
110-
}
92+
// "Ignore module.exports" tests:
93+
94+
it('should NOT throw when assigning unmatched named function to module.exports', function() {
95+
assertNoErrors('var module; module.exports = function foo(name) {};');
96+
});
97+
98+
it('should NOT throw when assigning unmatched named function to module["exports"]', function() {
99+
assertNoErrors('var module; module["exports"] = function foo(name) {};');
100+
});
101+
102+
it('should report function name when assigning unmatched named function to module ' +
103+
'which is a property of other variable', function() {
104+
assertErrorForMemberNameMismatch('var foo = {module:{}}; foo.module.exports = function bar(name) {};');
105+
});
106+
});
107+
108+
describe('option includeModuleExports: true', function() {
109+
beforeEach(function() {
110+
checker.configure({ requireMatchingFunctionName: { includeModuleExports: true } });
111+
});
112+
113+
it('should report function name when assigning unmatched named function to module.exports', function() {
114+
assertErrorForMemberNameMismatch('var module; module.exports = function foo(name) {};');
115+
});
116+
117+
it('should report function name when assigning unmatched named function to module["exports"]', function() {
118+
assertErrorForMemberNameMismatch('var module; module["exports"] = function foo(name) {};');
119+
});
120+
111121
});
122+
123+
function assertErrorForMemberNameMismatch(js) {
124+
assertError(js, 'Function name does not match member name');
125+
}
126+
127+
function assertErrorForPropertyNameMismatch(js) {
128+
assertError(js, 'Function name does not match property name');
129+
}
130+
131+
function assertError(js, message) {
132+
var errors = checker.checkString(js).getErrorList();
133+
expect(errors.length).to.be.at.least(1);
134+
expect(errors[0].rule).to.equal('requireMatchingFunctionName');
135+
expect(errors[0].message).to.equal(message);
136+
}
137+
138+
function assertNoErrors(js) {
139+
var errors = checker.checkString(js).getErrorList();
140+
expect(errors.length).to.equal(0);
141+
}
112142
});

0 commit comments

Comments
 (0)