UNPKG

3.49 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to flag blocks with no reason to exist
3 * @author Brandon Mills
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Rule Definition
10//------------------------------------------------------------------------------
11
12module.exports = {
13 meta: {
14 docs: {
15 description: "disallow unnecessary nested blocks",
16 category: "Best Practices",
17 recommended: false,
18 url: "https://eslint.org/docs/rules/no-lone-blocks"
19 },
20
21 schema: []
22 },
23
24 create(context) {
25
26 // A stack of lone blocks to be checked for block-level bindings
27 const loneBlocks = [];
28 let ruleDef;
29
30 /**
31 * Reports a node as invalid.
32 * @param {ASTNode} node - The node to be reported.
33 * @returns {void}
34 */
35 function report(node) {
36 const message = node.parent.type === "BlockStatement" ? "Nested block is redundant." : "Block is redundant.";
37
38 context.report({ node, message });
39 }
40
41 /**
42 * Checks for any ocurrence of a BlockStatement in a place where lists of statements can appear
43 * @param {ASTNode} node The node to check
44 * @returns {boolean} True if the node is a lone block.
45 */
46 function isLoneBlock(node) {
47 return node.parent.type === "BlockStatement" ||
48 node.parent.type === "Program" ||
49
50 // Don't report blocks in switch cases if the block is the only statement of the case.
51 node.parent.type === "SwitchCase" && !(node.parent.consequent[0] === node && node.parent.consequent.length === 1);
52 }
53
54 /**
55 * Checks the enclosing block of the current node for block-level bindings,
56 * and "marks it" as valid if any.
57 * @returns {void}
58 */
59 function markLoneBlock() {
60 if (loneBlocks.length === 0) {
61 return;
62 }
63
64 const block = context.getAncestors().pop();
65
66 if (loneBlocks[loneBlocks.length - 1] === block) {
67 loneBlocks.pop();
68 }
69 }
70
71 // Default rule definition: report all lone blocks
72 ruleDef = {
73 BlockStatement(node) {
74 if (isLoneBlock(node)) {
75 report(node);
76 }
77 }
78 };
79
80 // ES6: report blocks without block-level bindings
81 if (context.parserOptions.ecmaVersion >= 6) {
82 ruleDef = {
83 BlockStatement(node) {
84 if (isLoneBlock(node)) {
85 loneBlocks.push(node);
86 }
87 },
88 "BlockStatement:exit"(node) {
89 if (loneBlocks.length > 0 && loneBlocks[loneBlocks.length - 1] === node) {
90 loneBlocks.pop();
91 report(node);
92 }
93 }
94 };
95
96 ruleDef.VariableDeclaration = function(node) {
97 if (node.kind === "let" || node.kind === "const") {
98 markLoneBlock();
99 }
100 };
101
102 ruleDef.FunctionDeclaration = function() {
103 if (context.getScope().isStrict) {
104 markLoneBlock();
105 }
106 };
107
108 ruleDef.ClassDeclaration = markLoneBlock;
109 }
110
111 return ruleDef;
112 }
113};