137 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			137 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * @fileoverview Rule to flag blocks with no reason to exist
 | |
|  * @author Brandon Mills
 | |
|  */
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| //------------------------------------------------------------------------------
 | |
| // Rule Definition
 | |
| //------------------------------------------------------------------------------
 | |
| 
 | |
| /** @type {import('../shared/types').Rule} */
 | |
| module.exports = {
 | |
|     meta: {
 | |
|         type: "suggestion",
 | |
| 
 | |
|         docs: {
 | |
|             description: "Disallow unnecessary nested blocks",
 | |
|             recommended: false,
 | |
|             url: "https://eslint.org/docs/latest/rules/no-lone-blocks"
 | |
|         },
 | |
| 
 | |
|         schema: [],
 | |
| 
 | |
|         messages: {
 | |
|             redundantBlock: "Block is redundant.",
 | |
|             redundantNestedBlock: "Nested block is redundant."
 | |
|         }
 | |
|     },
 | |
| 
 | |
|     create(context) {
 | |
| 
 | |
|         // A stack of lone blocks to be checked for block-level bindings
 | |
|         const loneBlocks = [];
 | |
|         let ruleDef;
 | |
|         const sourceCode = context.sourceCode;
 | |
| 
 | |
|         /**
 | |
|          * Reports a node as invalid.
 | |
|          * @param {ASTNode} node The node to be reported.
 | |
|          * @returns {void}
 | |
|          */
 | |
|         function report(node) {
 | |
|             const messageId = node.parent.type === "BlockStatement" || node.parent.type === "StaticBlock"
 | |
|                 ? "redundantNestedBlock"
 | |
|                 : "redundantBlock";
 | |
| 
 | |
|             context.report({
 | |
|                 node,
 | |
|                 messageId
 | |
|             });
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * Checks for any occurrence 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 === "StaticBlock" ||
 | |
|                 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.
 | |
|          * @param {ASTNode} node The current node to check.
 | |
|          * @returns {void}
 | |
|          */
 | |
|         function markLoneBlock(node) {
 | |
|             if (loneBlocks.length === 0) {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             const block = node.parent;
 | |
| 
 | |
|             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, or that's only child of another block
 | |
|         if (context.languageOptions.ecmaVersion >= 2015) {
 | |
|             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);
 | |
|                     } else if (
 | |
|                         (
 | |
|                             node.parent.type === "BlockStatement" ||
 | |
|                             node.parent.type === "StaticBlock"
 | |
|                         ) &&
 | |
|                         node.parent.body.length === 1
 | |
|                     ) {
 | |
|                         report(node);
 | |
|                     }
 | |
|                 }
 | |
|             };
 | |
| 
 | |
|             ruleDef.VariableDeclaration = function(node) {
 | |
|                 if (node.kind === "let" || node.kind === "const") {
 | |
|                     markLoneBlock(node);
 | |
|                 }
 | |
|             };
 | |
| 
 | |
|             ruleDef.FunctionDeclaration = function(node) {
 | |
|                 if (sourceCode.getScope(node).isStrict) {
 | |
|                     markLoneBlock(node);
 | |
|                 }
 | |
|             };
 | |
| 
 | |
|             ruleDef.ClassDeclaration = markLoneBlock;
 | |
|         }
 | |
| 
 | |
|         return ruleDef;
 | |
|     }
 | |
| };
 |