| /** |
| * @fileoverview Rule to flag blocks with no reason to exist |
| * @author Brandon Mills |
| */ |
| |
| "use strict"; |
| |
| //------------------------------------------------------------------------------ |
| // Rule Definition |
| //------------------------------------------------------------------------------ |
| |
| module.exports = { |
| meta: { |
| type: "suggestion", |
| |
| docs: { |
| description: "disallow unnecessary nested blocks", |
| category: "Best Practices", |
| recommended: false, |
| url: "https://eslint.org/docs/rules/no-lone-blocks" |
| }, |
| |
| schema: [] |
| }, |
| |
| create(context) { |
| |
| // A stack of lone blocks to be checked for block-level bindings |
| const loneBlocks = []; |
| let ruleDef; |
| |
| /** |
| * Reports a node as invalid. |
| * @param {ASTNode} node - The node to be reported. |
| * @returns {void} |
| */ |
| function report(node) { |
| const message = node.parent.type === "BlockStatement" ? "Nested block is redundant." : "Block is redundant."; |
| |
| context.report({ node, message }); |
| } |
| |
| /** |
| * Checks for any ocurrence of a BlockStatement in a place where lists of statements can appear |
| * @param {ASTNode} node The node to check |
| * @returns {boolean} True if the node is a lone block. |
| */ |
| function isLoneBlock(node) { |
| return node.parent.type === "BlockStatement" || |
| node.parent.type === "Program" || |
| |
| // Don't report blocks in switch cases if the block is the only statement of the case. |
| node.parent.type === "SwitchCase" && !(node.parent.consequent[0] === node && node.parent.consequent.length === 1); |
| } |
| |
| /** |
| * Checks the enclosing block of the current node for block-level bindings, |
| * and "marks it" as valid if any. |
| * @returns {void} |
| */ |
| function markLoneBlock() { |
| if (loneBlocks.length === 0) { |
| return; |
| } |
| |
| const block = context.getAncestors().pop(); |
| |
| if (loneBlocks[loneBlocks.length - 1] === block) { |
| loneBlocks.pop(); |
| } |
| } |
| |
| // Default rule definition: report all lone blocks |
| ruleDef = { |
| BlockStatement(node) { |
| if (isLoneBlock(node)) { |
| report(node); |
| } |
| } |
| }; |
| |
| // ES6: report blocks without block-level bindings |
| if (context.parserOptions.ecmaVersion >= 6) { |
| ruleDef = { |
| BlockStatement(node) { |
| if (isLoneBlock(node)) { |
| loneBlocks.push(node); |
| } |
| }, |
| "BlockStatement:exit"(node) { |
| if (loneBlocks.length > 0 && loneBlocks[loneBlocks.length - 1] === node) { |
| loneBlocks.pop(); |
| report(node); |
| } |
| } |
| }; |
| |
| ruleDef.VariableDeclaration = function(node) { |
| if (node.kind === "let" || node.kind === "const") { |
| markLoneBlock(); |
| } |
| }; |
| |
| ruleDef.FunctionDeclaration = function() { |
| if (context.getScope().isStrict) { |
| markLoneBlock(); |
| } |
| }; |
| |
| ruleDef.ClassDeclaration = markLoneBlock; |
| } |
| |
| return ruleDef; |
| } |
| }; |