• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * @fileoverview Rule to disallow async functions which have no `await` expression.
3 * @author Toru Nagashima
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("./utils/ast-utils");
13
14//------------------------------------------------------------------------------
15// Helpers
16//------------------------------------------------------------------------------
17
18/**
19 * Capitalize the 1st letter of the given text.
20 * @param {string} text The text to capitalize.
21 * @returns {string} The text that the 1st letter was capitalized.
22 */
23function capitalizeFirstLetter(text) {
24    return text[0].toUpperCase() + text.slice(1);
25}
26
27//------------------------------------------------------------------------------
28// Rule Definition
29//------------------------------------------------------------------------------
30
31module.exports = {
32    meta: {
33        type: "suggestion",
34
35        docs: {
36            description: "disallow async functions which have no `await` expression",
37            category: "Best Practices",
38            recommended: false,
39            url: "https://eslint.org/docs/rules/require-await"
40        },
41
42        schema: [],
43
44        messages: {
45            missingAwait: "{{name}} has no 'await' expression."
46        }
47    },
48
49    create(context) {
50        const sourceCode = context.getSourceCode();
51        let scopeInfo = null;
52
53        /**
54         * Push the scope info object to the stack.
55         * @returns {void}
56         */
57        function enterFunction() {
58            scopeInfo = {
59                upper: scopeInfo,
60                hasAwait: false
61            };
62        }
63
64        /**
65         * Pop the top scope info object from the stack.
66         * Also, it reports the function if needed.
67         * @param {ASTNode} node The node to report.
68         * @returns {void}
69         */
70        function exitFunction(node) {
71            if (!node.generator && node.async && !scopeInfo.hasAwait && !astUtils.isEmptyFunction(node)) {
72                context.report({
73                    node,
74                    loc: astUtils.getFunctionHeadLoc(node, sourceCode),
75                    messageId: "missingAwait",
76                    data: {
77                        name: capitalizeFirstLetter(
78                            astUtils.getFunctionNameWithKind(node)
79                        )
80                    }
81                });
82            }
83
84            scopeInfo = scopeInfo.upper;
85        }
86
87        return {
88            FunctionDeclaration: enterFunction,
89            FunctionExpression: enterFunction,
90            ArrowFunctionExpression: enterFunction,
91            "FunctionDeclaration:exit": exitFunction,
92            "FunctionExpression:exit": exitFunction,
93            "ArrowFunctionExpression:exit": exitFunction,
94
95            AwaitExpression() {
96                if (!scopeInfo) {
97                    return;
98                }
99
100                scopeInfo.hasAwait = true;
101            },
102            ForOfStatement(node) {
103                if (!scopeInfo) {
104                    return;
105                }
106
107                if (node.await) {
108                    scopeInfo.hasAwait = true;
109                }
110            }
111        };
112    }
113};
114