123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- /**
- * @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/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;
- /**
- * 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.
- * @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, 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();
- }
- };
- ruleDef.FunctionDeclaration = function() {
- if (context.getScope().isStrict) {
- markLoneBlock();
- }
- };
- ruleDef.ClassDeclaration = markLoneBlock;
- }
- return ruleDef;
- }
- };
|