| /** |
| * @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 |
| }; |
| } |
| }; |