Skip to content

Commit 094ef6a

Browse files
sudoworkmichalsnik
authored andcommitted
Allow concise ArrowFunctionExpression (named-functions-in-promises) (#64)
* Allow concise ArrowFunctionExpression (named-functions-in-promises) * Ensure CallExpression is calling a method on the parent object This is to ensure that the promise handler is defined as a method on the parent object and not some other object. * Fix destructuring * Drop requirement that method is called on this * Make allowing concise arrow expressions optional * Document allowConciseArrow option (named-functions-in-promises) * Address Code Review Comments - Rename `allowConciseArrow` to `allowSimpleArrowFunction` - Update documentation for `allowSimpleArrowFunction` - Move `isConciseArrowFunctionWithCallExpression` to utils - Add unit test for `isConciseArrowFunctionWithCallExpression` - Add invalid test cases for arrow functions with bodies when `allowSimpleArrowFunction` is set to true * Update isConciseArrowFunctionWithCallExpression test name * Add test case for block body
1 parent 6a47c88 commit 094ef6a

File tree

5 files changed

+136
-8
lines changed

5 files changed

+136
-8
lines changed

docs/rules/named-functions-in-promises.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@
22

33
### Rule name: `named-functions-in-promises`
44

5+
#### Configuration
6+
7+
```
8+
ember/named-functions-in-promises: [2, {
9+
allowSimpleArrowFunction: false,
10+
}]
11+
```
12+
13+
Setting `allowSimpleArrowFunction` to `true` allows arrow function expressions that do not have block bodies.
14+
These simple arrow functions must also only contain a single function call.
15+
For example: `.then(user => this._reloadUser(user))`.
16+
17+
#### Description
18+
519
When you use promises and its handlers, use named functions defined on parent object. Thus, you will be able to test them in isolation using unit tests without any additional mocking.
620

721
```javascript

lib/rules/named-functions-in-promises.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ module.exports = {
1212
},
1313

1414
create(context) {
15+
const options = context.options[0] || {};
16+
const allowSimpleArrowFunction = options.allowSimpleArrowFunction || false;
17+
1518
const message = 'Use named functions defined on objects to handle promises';
1619

1720
const report = function (node) {
@@ -22,14 +25,19 @@ module.exports = {
2225
CallExpression(node) {
2326
const firstArg = node.arguments[0];
2427

25-
if (
26-
hasPromiseExpression(node) &&
27-
(
28+
if (hasPromiseExpression(node)) {
29+
if (
30+
allowSimpleArrowFunction &&
31+
utils.isConciseArrowFunctionWithCallExpression(firstArg)
32+
) {
33+
return;
34+
}
35+
if (
2836
utils.isFunctionExpression(firstArg) ||
2937
utils.isArrowFunctionExpression(firstArg)
30-
)
31-
) {
32-
report(node);
38+
) {
39+
report(node);
40+
}
3341
}
3442
},
3543
};

lib/utils/utils.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = {
1010
isArrayExpression,
1111
isFunctionExpression,
1212
isArrowFunctionExpression,
13+
isConciseArrowFunctionWithCallExpression,
1314
isNewExpression,
1415
isCallWithFunctionExpression,
1516
isThisExpression,
@@ -118,6 +119,20 @@ function isArrowFunctionExpression(node) {
118119
return node !== undefined && node.type === 'ArrowFunctionExpression';
119120
}
120121

122+
/**
123+
* Check whether or not a node is an ArrowFunctionExpression with concise body
124+
* that contains a call expression.
125+
*
126+
* @param {Object} node The node to check.
127+
* @returns {boolean} Whether or not the node is an ArrowFunctionExpression
128+
* with concise body.
129+
*/
130+
function isConciseArrowFunctionWithCallExpression(node) {
131+
return isArrowFunctionExpression(node) &&
132+
node.expression &&
133+
isCallExpression(node.body);
134+
}
135+
121136
/**
122137
* Check whether or not a node is an NewExpression.
123138
*

tests/lib/rules/named-functions-in-promises.js

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,30 @@ eslintTester.run('named-functions-in-promises', rule, {
3939
}, {
4040
code: 'user.save().finally(_finallyDo);',
4141
parserOptions: { ecmaVersion: 6 },
42+
}, {
43+
code: 'user.save().then(() => this._reloadUser(user));',
44+
parserOptions: { ecmaVersion: 6 },
45+
options: [{
46+
allowSimpleArrowFunction: true,
47+
}],
48+
}, {
49+
code: 'user.save().catch(err => this._handleError(err));',
50+
parserOptions: { ecmaVersion: 6 },
51+
options: [{
52+
allowSimpleArrowFunction: true,
53+
}],
54+
}, {
55+
code: 'user.save().finally(() => this._finallyDo());',
56+
parserOptions: { ecmaVersion: 6 },
57+
options: [{
58+
allowSimpleArrowFunction: true,
59+
}],
60+
}, {
61+
code: 'user.save().then(() => user.reload());',
62+
parserOptions: { ecmaVersion: 6 },
63+
options: [{
64+
allowSimpleArrowFunction: true,
65+
}],
4266
},
4367
],
4468
invalid: [
@@ -49,23 +73,77 @@ eslintTester.run('named-functions-in-promises', rule, {
4973
message: 'Use named functions defined on objects to handle promises',
5074
}],
5175
}, {
52-
code: 'user.save().then(() => user.reload());',
76+
code: 'user.save().catch(() => {return error.handle();});',
5377
parserOptions: { ecmaVersion: 6 },
5478
errors: [{
5579
message: 'Use named functions defined on objects to handle promises',
5680
}],
81+
}, {
82+
code: 'user.save().finally(() => {return finallyDo();});',
83+
parserOptions: { ecmaVersion: 6 },
84+
errors: [{
85+
message: 'Use named functions defined on objects to handle promises',
86+
}],
87+
}, {
88+
code: 'user.save().then(() => {return user.reload();});',
89+
parserOptions: { ecmaVersion: 6 },
90+
options: [{
91+
allowSimpleArrowFunction: true,
92+
}],
93+
errors: [{
94+
message: 'Use named functions defined on objects to handle promises',
95+
}],
5796
}, {
5897
code: 'user.save().catch(() => {return error.handle();});',
5998
parserOptions: { ecmaVersion: 6 },
99+
options: [{
100+
allowSimpleArrowFunction: true,
101+
}],
60102
errors: [{
61103
message: 'Use named functions defined on objects to handle promises',
62104
}],
63105
}, {
64106
code: 'user.save().finally(() => {return finallyDo();});',
65107
parserOptions: { ecmaVersion: 6 },
108+
options: [{
109+
allowSimpleArrowFunction: true,
110+
}],
66111
errors: [{
67112
message: 'Use named functions defined on objects to handle promises',
68113
}],
69-
},
114+
}, {
115+
code: 'user.save().then(() => this._reloadUser(user));',
116+
parserOptions: { ecmaVersion: 6 },
117+
errors: [{
118+
message: 'Use named functions defined on objects to handle promises',
119+
}],
120+
}, {
121+
code: 'user.save().catch(err => this._handleError(err));',
122+
parserOptions: { ecmaVersion: 6 },
123+
errors: [{
124+
message: 'Use named functions defined on objects to handle promises',
125+
}],
126+
}, {
127+
code: 'user.save().finally(() => this._finallyDo());',
128+
parserOptions: { ecmaVersion: 6 },
129+
errors: [{
130+
message: 'Use named functions defined on objects to handle promises',
131+
}],
132+
}, {
133+
code: 'user.save().then(() => user.reload());',
134+
parserOptions: { ecmaVersion: 6 },
135+
errors: [{
136+
message: 'Use named functions defined on objects to handle promises',
137+
}],
138+
}, {
139+
code: 'user.save().then(user => user.name);',
140+
parserOptions: { ecmaVersion: 6 },
141+
options: [{
142+
allowSimpleArrowFunction: true,
143+
}],
144+
errors: [{
145+
message: 'Use named functions defined on objects to handle promises',
146+
}],
147+
}
70148
],
71149
});

tests/lib/utils/utils-test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,19 @@ describe('isArrowFunctionExpression', () => {
9494
});
9595
});
9696

97+
describe('isConciseArrowFunctionExpressionWithCall', () => {
98+
const node = parse('test = () => foo()').right;
99+
const blockNode = parse('test = () => { foo() }').right;
100+
101+
it('should check if node is concise arrow function expression with call expression body', () => {
102+
assert.ok(utils.isConciseArrowFunctionWithCallExpression(node));
103+
});
104+
105+
it('should check if node does not have block body', () => {
106+
assert.ok(!utils.isConciseArrowFunctionWithCallExpression(blockNode));
107+
});
108+
});
109+
97110
describe('isNewExpression', () => {
98111
const node = parse('new Date()');
99112

0 commit comments

Comments
 (0)