/** * @fileoverview Rule to disallow uses of await inside of loops. * @author Nat Mote (nmote) */ "use strict"; /** * Check whether it should stop traversing ancestors at the given node. * @param {ASTNode} node A node to check. * @returns {boolean} `true` if it should stop traversing. */ function isBoundary(node) { const t = node.type; return ( t === "FunctionDeclaration" || t === "FunctionExpression" || t === "ArrowFunctionExpression" || /* * Don't report the await expressions on for-await-of loop since it's * asynchronous iteration intentionally. */ (t === "ForOfStatement" && node.await === true) ); } /** * Check whether the given node is in loop. * @param {ASTNode} node A node to check. * @param {ASTNode} parent A parent node to check. * @returns {boolean} `true` if the node is in loop. */ function isLooped(node, parent) { switch (parent.type) { case "ForStatement": return ( node === parent.test || node === parent.update || node === parent.body ); case "ForOfStatement": case "ForInStatement": return node === parent.body; case "WhileStatement": case "DoWhileStatement": return node === parent.test || node === parent.body; default: return false; } } module.exports = { meta: { type: "problem", docs: { description: "disallow `await` inside of loops", category: "Possible Errors", recommended: false, url: "https://eslint.org/docs/rules/no-await-in-loop" }, schema: [], messages: { unexpectedAwait: "Unexpected `await` inside a loop." } }, create(context) { /** * Validate an await expression. * @param {ASTNode} awaitNode An AwaitExpression or ForOfStatement node to validate. * @returns {void} */ function validate(awaitNode) { if (awaitNode.type === "ForOfStatement" && !awaitNode.await) { return; } let node = awaitNode; let parent = node.parent; while (parent && !isBoundary(parent)) { if (isLooped(node, parent)) { context.report({ node: awaitNode, messageId: "unexpectedAwait" }); return; } node = parent; parent = parent.parent; } } return { AwaitExpression: validate, ForOfStatement: validate }; } };