]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
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 | ||
34eeec05 | 12 | /** @type {import('../shared/types').Rule} */ |
eb39fafa DC |
13 | module.exports = { |
14 | meta: { | |
15 | type: "suggestion", | |
16 | ||
17 | docs: { | |
18 | description: "disallow unnecessary nested blocks", | |
eb39fafa DC |
19 | recommended: false, |
20 | url: "https://eslint.org/docs/rules/no-lone-blocks" | |
21 | }, | |
22 | ||
23 | schema: [], | |
24 | ||
25 | messages: { | |
26 | redundantBlock: "Block is redundant.", | |
27 | redundantNestedBlock: "Nested block is redundant." | |
28 | } | |
29 | }, | |
30 | ||
31 | create(context) { | |
32 | ||
33 | // A stack of lone blocks to be checked for block-level bindings | |
34 | const loneBlocks = []; | |
35 | let ruleDef; | |
36 | ||
37 | /** | |
38 | * Reports a node as invalid. | |
39 | * @param {ASTNode} node The node to be reported. | |
40 | * @returns {void} | |
41 | */ | |
42 | function report(node) { | |
609c276f TL |
43 | const messageId = node.parent.type === "BlockStatement" || node.parent.type === "StaticBlock" |
44 | ? "redundantNestedBlock" | |
45 | : "redundantBlock"; | |
eb39fafa DC |
46 | |
47 | context.report({ | |
48 | node, | |
49 | messageId | |
50 | }); | |
51 | } | |
52 | ||
53 | /** | |
56c4a2cb | 54 | * Checks for any occurrence of a BlockStatement in a place where lists of statements can appear |
eb39fafa DC |
55 | * @param {ASTNode} node The node to check |
56 | * @returns {boolean} True if the node is a lone block. | |
57 | */ | |
58 | function isLoneBlock(node) { | |
59 | return node.parent.type === "BlockStatement" || | |
609c276f | 60 | node.parent.type === "StaticBlock" || |
eb39fafa DC |
61 | node.parent.type === "Program" || |
62 | ||
63 | // Don't report blocks in switch cases if the block is the only statement of the case. | |
64 | node.parent.type === "SwitchCase" && !(node.parent.consequent[0] === node && node.parent.consequent.length === 1); | |
65 | } | |
66 | ||
67 | /** | |
68 | * Checks the enclosing block of the current node for block-level bindings, | |
69 | * and "marks it" as valid if any. | |
70 | * @returns {void} | |
71 | */ | |
72 | function markLoneBlock() { | |
73 | if (loneBlocks.length === 0) { | |
74 | return; | |
75 | } | |
76 | ||
77 | const block = context.getAncestors().pop(); | |
78 | ||
79 | if (loneBlocks[loneBlocks.length - 1] === block) { | |
80 | loneBlocks.pop(); | |
81 | } | |
82 | } | |
83 | ||
84 | // Default rule definition: report all lone blocks | |
85 | ruleDef = { | |
86 | BlockStatement(node) { | |
87 | if (isLoneBlock(node)) { | |
88 | report(node); | |
89 | } | |
90 | } | |
91 | }; | |
92 | ||
93 | // ES6: report blocks without block-level bindings, or that's only child of another block | |
94 | if (context.parserOptions.ecmaVersion >= 6) { | |
95 | ruleDef = { | |
96 | BlockStatement(node) { | |
97 | if (isLoneBlock(node)) { | |
98 | loneBlocks.push(node); | |
99 | } | |
100 | }, | |
101 | "BlockStatement:exit"(node) { | |
102 | if (loneBlocks.length > 0 && loneBlocks[loneBlocks.length - 1] === node) { | |
103 | loneBlocks.pop(); | |
104 | report(node); | |
105 | } else if ( | |
609c276f TL |
106 | ( |
107 | node.parent.type === "BlockStatement" || | |
108 | node.parent.type === "StaticBlock" | |
109 | ) && | |
eb39fafa DC |
110 | node.parent.body.length === 1 |
111 | ) { | |
112 | report(node); | |
113 | } | |
114 | } | |
115 | }; | |
116 | ||
117 | ruleDef.VariableDeclaration = function(node) { | |
118 | if (node.kind === "let" || node.kind === "const") { | |
119 | markLoneBlock(); | |
120 | } | |
121 | }; | |
122 | ||
123 | ruleDef.FunctionDeclaration = function() { | |
124 | if (context.getScope().isStrict) { | |
125 | markLoneBlock(); | |
126 | } | |
127 | }; | |
128 | ||
129 | ruleDef.ClassDeclaration = markLoneBlock; | |
130 | } | |
131 | ||
132 | return ruleDef; | |
133 | } | |
134 | }; |