Skip to content

Commit 9fa8d20

Browse files
addaleaxtargos
authored andcommitted
tools: add linting rule for async IIFEs
The result of an async IIFE should always be handled in our tests, typically by adding `.then(common.mustCall())` to verify that the async function actually finishes executing at some point. PR-URL: #34363 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Richard Lau <riclau@uk.ibm.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
1 parent 89ee6ab commit 9fa8d20

File tree

3 files changed

+90
-0
lines changed

3 files changed

+90
-0
lines changed

test/.eslintrc.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ rules:
5353
node-core/prefer-common-mustsucceed: error
5454
node-core/crypto-check: error
5555
node-core/eslint-check: error
56+
node-core/async-iife-no-unused-result: error
5657
node-core/inspector-check: error
5758
## common module is mandatory in tests
5859
node-core/required-modules:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'use strict';
2+
const common = require('../common');
3+
if (!common.hasCrypto)
4+
common.skip('missing crypto');
5+
common.skipIfEslintMissing();
6+
7+
const RuleTester = require('../../tools/node_modules/eslint').RuleTester;
8+
const rule = require('../../tools/eslint-rules/async-iife-no-unused-result');
9+
10+
const message = 'The result of an immediately-invoked async function needs ' +
11+
'to be used (e.g. with `.then(common.mustCall())`)';
12+
13+
const tester = new RuleTester({ parserOptions: { ecmaVersion: 8 } });
14+
tester.run('async-iife-no-unused-result', rule, {
15+
valid: [
16+
'(() => {})()',
17+
'(async () => {})',
18+
'(async () => {})().then()',
19+
'(async () => {})().catch()',
20+
'(function () {})()',
21+
'(async function () {})',
22+
'(async function () {})().then()',
23+
'(async function () {})().catch()',
24+
],
25+
invalid: [
26+
{
27+
code: '(async () => {})()',
28+
errors: [{ message }],
29+
output: '(async () => {})()',
30+
},
31+
{
32+
code: '(async function() {})()',
33+
errors: [{ message }],
34+
output: '(async function() {})()',
35+
},
36+
{
37+
code: "const common = require('../common');(async () => {})()",
38+
errors: [{ message }],
39+
output: "const common = require('../common');(async () => {})()" +
40+
'.then(common.mustCall())',
41+
},
42+
{
43+
code: "const common = require('../common');(async function() {})()",
44+
errors: [{ message }],
45+
output: "const common = require('../common');(async function() {})()" +
46+
'.then(common.mustCall())',
47+
},
48+
]
49+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use strict';
2+
const { isCommonModule } = require('./rules-utils.js');
3+
4+
function isAsyncIIFE(node) {
5+
const { callee: { type, async } } = node;
6+
const types = ['FunctionExpression', 'ArrowFunctionExpression'];
7+
return types.includes(type) && async;
8+
}
9+
10+
const message =
11+
'The result of an immediately-invoked async function needs to be used ' +
12+
'(e.g. with `.then(common.mustCall())`)';
13+
14+
module.exports = {
15+
meta: {
16+
fixable: 'code'
17+
},
18+
create: function(context) {
19+
let hasCommonModule = false;
20+
return {
21+
CallExpression: function(node) {
22+
if (isCommonModule(node) && node.parent.type === 'VariableDeclarator') {
23+
hasCommonModule = true;
24+
}
25+
26+
if (!isAsyncIIFE(node)) return;
27+
if (node.parent && node.parent.type === 'ExpressionStatement') {
28+
context.report({
29+
node,
30+
message,
31+
fix: (fixer) => {
32+
if (hasCommonModule)
33+
return fixer.insertTextAfter(node, '.then(common.mustCall())');
34+
}
35+
});
36+
}
37+
}
38+
};
39+
}
40+
};

0 commit comments

Comments
 (0)