code:
/**
 * @fileoverview Rule to flag use of an empty block statement
 * @author Nicholas C. Zakas
 */
"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        hasSuggestions: true,
        type: "suggestion",

        docs: {
            description: "Disallow empty block statements",
            recommended: true,
            url: "https://eslint.org/docs/latest/rules/no-empty"
        },

        schema: [
            {
                type: "object",
                properties: {
                    allowEmptyCatch: {
                        type: "boolean",
                        default: false
                    }
                },
                additionalProperties: false
            }
        ],

        messages: {
            unexpected: "Empty {{type}} statement.",
            suggestComment: "Add comment inside empty {{type}} statement."
        }
    },

    create(context) {
        const options = context.options[0] || {},
            allowEmptyCatch = options.allowEmptyCatch || false;

        const sourceCode = context.sourceCode;

        return {
            BlockStatement(node) {

                // if the body is not empty, we can just return immediately
                if (node.body.length !== 0) {
                    return;
                }

                // a function is generally allowed to be empty
                if (astUtils.isFunction(node.parent)) {
                    return;
                }

                if (allowEmptyCatch && node.parent.type === "CatchClause") {
                    return;
                }

                // any other block is only allowed to be empty, if it contains a comment
                if (sourceCode.getCommentsInside(node).length > 0) {
                    return;
                }

                context.report({
                    node,
                    messageId: "unexpected",
                    data: { type: "block" },
                    suggest: [
                        {
                            messageId: "suggestComment",
                            data: { type: "block" },
                            fix(fixer) {
                                const range = [node.range[0] + 1, node.range[1] - 1];

                                return fixer.replaceTextRange(range, " /* empty */ ");
                            }
                        }
                    ]
                });
            },

            SwitchStatement(node) {

                if (typeof node.cases === "undefined" || node.cases.length === 0) {
                    context.report({ node, messageId: "unexpected", data: { type: "switch" } });
                }
            }
        };

    }
};


code:
/**
 * @fileoverview Rule to flag comparisons to null without a type-checking
 * operator.
 * @author Ian Christian Myers
 */

"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow `null` comparisons without type-checking operators",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/no-eq-null"
        },

        schema: [],

        messages: {
            unexpected: "Use '===' to compare with null."
        }
    },

    create(context) {

        return {

            BinaryExpression(node) {
                const badOperator = node.operator === "==" || node.operator === "!=";

                if (node.right.type === "Literal" && node.right.raw === "null" && badOperator ||
                        node.left.type === "Literal" && node.left.raw === "null" && badOperator) {
                    context.report({ node, messageId: "unexpected" });
                }
            }
        };

    }
};

license: mit


code:
/**
 * @fileoverview Rule to flag use of eval() statement
 * @author Nicholas C. Zakas
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

const candidatesOfGlobalObject = Object.freeze([
    "global",
    "window",
    "globalThis"
]);

/**
 * Checks a given node is a MemberExpression node which has the specified name's
 * property.
 * @param {ASTNode} node A node to check.
 * @param {string} name A name to check.
 * @returns {boolean} `true` if the node is a MemberExpression node which has
 *      the specified name's property
 */
function isMember(node, name) {
    return astUtils.isSpecificMemberAccess(node, null, name);
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow the use of `eval()`",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/no-eval"
        },

        schema: [
            {
                type: "object",
                properties: {
                    allowIndirect: { type: "boolean", default: false }
                },
                additionalProperties: false
            }
        ],

        messages: {
            unexpected: "eval can be harmful."
        }
    },

    create(context) {
        const allowIndirect = Boolean(
            context.options[0] &&
            context.options[0].allowIndirect
        );
        const sourceCode = context.sourceCode;
        let funcInfo = null;

        /**
         * Pushes a `this` scope (non-arrow function, class static block, or class field initializer) information to the stack.
         * Top-level scopes are handled separately.
         *
         * This is used in order to check whether or not `this` binding is a
         * reference to the global object.
         * @param {ASTNode} node A node of the scope.
         *      For functions, this is one of FunctionDeclaration, FunctionExpression.
         *      For class static blocks, this is StaticBlock.
         *      For class field initializers, this can be any node that is PropertyDefinition#value.
         * @returns {void}
         */
        function enterThisScope(node) {
            const strict = sourceCode.getScope(node).isStrict;

            funcInfo = {
                upper: funcInfo,
                node,
                strict,
                isTopLevelOfScript: false,
                defaultThis: false,
                initialized: strict
            };
        }

        /**
         * Pops a variable scope from the stack.
         * @returns {void}
         */
        function exitThisScope() {
            funcInfo = funcInfo.upper;
        }

        /**
         * Reports a given node.
         *
         * `node` is `Identifier` or `MemberExpression`.
         * The parent of `node` might be `CallExpression`.
         *
         * The location of the report is always `eval` `Identifier` (or possibly
         * `Literal`). The type of the report is `CallExpression` if the parent is
         * `CallExpression`. Otherwise, it's the given node type.
         * @param {ASTNode} node A node to report.
         * @returns {void}
         */
        function report(node) {
            const parent = node.parent;
            const locationNode = node.type === "MemberExpression"
                ? node.property
                : node;

            const reportNode = parent.type === "CallExpression" && parent.callee === node
                ? parent
                : node;

            context.report({
                node: reportNode,
                loc: locationNode.loc,
                messageId: "unexpected"
            });
        }

        /**
         * Reports accesses of `eval` via the global object.
         * @param {eslint-scope.Scope} globalScope The global scope.
         * @returns {void}
         */
        function reportAccessingEvalViaGlobalObject(globalScope) {
            for (let i = 0; i < candidatesOfGlobalObject.length; ++i) {
                const name = candidatesOfGlobalObject[i];
                const variable = astUtils.getVariableByName(globalScope, name);

                if (!variable) {
                    continue;
                }

                const references = variable.references;

                for (let j = 0; j < references.length; ++j) {
                    const identifier = references[j].identifier;
                    let node = identifier.parent;

                    // To detect code like `window.window.eval`.
                    while (isMember(node, name)) {
                        node = node.parent;
                    }

                    // Reports.
                    if (isMember(node, "eval")) {
                        report(node);
                    }
                }
            }
        }

        /**
         * Reports all accesses of `eval` (excludes direct calls to eval).
         * @param {eslint-scope.Scope} globalScope The global scope.
         * @returns {void}
         */
        function reportAccessingEval(globalScope) {
            const variable = astUtils.getVariableByName(globalScope, "eval");

            if (!variable) {
                return;
            }

            const references = variable.references;

            for (let i = 0; i < references.length; ++i) {
                const reference = references[i];
                const id = reference.identifier;

                if (id.name === "eval" && !astUtils.isCallee(id)) {

                    // Is accessing to eval (excludes direct calls to eval)
                    report(id);
                }
            }
        }

        if (allowIndirect) {

            // Checks only direct calls to eval. It's simple!
            return {
                "CallExpression:exit"(node) {
                    const callee = node.callee;

                    /*
                     * Optional call (`eval?.("code")`) is not direct eval.
                     * The direct eval is only step 6.a.vi of https://tc39.es/ecma262/#sec-function-calls-runtime-semantics-evaluation
                     * But the optional call is https://tc39.es/ecma262/#sec-optional-chaining-chain-evaluation
                     */
                    if (!node.optional && astUtils.isSpecificId(callee, "eval")) {
                        report(callee);
                    }
                }
            };
        }

        return {
            "CallExpression:exit"(node) {
                const callee = node.callee;

                if (astUtils.isSpecificId(callee, "eval")) {
                    report(callee);
                }
            },

            Program(node) {
                const scope = sourceCode.getScope(node),
                    features = context.parserOptions.ecmaFeatures || {},
                    strict =
                        scope.isStrict ||
                        node.sourceType === "module" ||
                        (features.globalReturn && scope.childScopes[0].isStrict),
                    isTopLevelOfScript = node.sourceType !== "module" && !features.globalReturn;

                funcInfo = {
                    upper: null,
                    node,
                    strict,
                    isTopLevelOfScript,
                    defaultThis: true,
                    initialized: true
                };
            },

            "Program:exit"(node) {
                const globalScope = sourceCode.getScope(node);

                exitThisScope();
                reportAccessingEval(globalScope);
                reportAccessingEvalViaGlobalObject(globalScope);
            },

            FunctionDeclaration: enterThisScope,
            "FunctionDeclaration:exit": exitThisScope,
            FunctionExpression: enterThisScope,
            "FunctionExpression:exit": exitThisScope,
            "PropertyDefinition > *.value": enterThisScope,
            "PropertyDefinition > *.value:exit": exitThisScope,
            StaticBlock: enterThisScope,
            "StaticBlock:exit": exitThisScope,

            ThisExpression(node) {
                if (!isMember(node.parent, "eval")) {
                    return;
                }

                /*
                 * `this.eval` is found.
                 * Checks whether or not the value of `this` is the global object.
                 */
                if (!funcInfo.initialized) {
                    funcInfo.initialized = true;
                    funcInfo.defaultThis = astUtils.isDefaultThisBinding(
                        funcInfo.node,
                        sourceCode
                    );
                }

                // `this` at the top level of scripts always refers to the global object
                if (funcInfo.isTopLevelOfScript || (!funcInfo.strict && funcInfo.defaultThis)) {

                    // `this.eval` is possible built-in `eval`.
                    report(node.parent);
                }
            }
        };

    }
};


code:
/**
 * @fileoverview Rule to flag assignment of the exception parameter
 * @author Stephen Murray <spmurrayzzz>
 */

"use strict";

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "problem",

        docs: {
            description: "Disallow reassigning exceptions in `catch` clauses",
            recommended: true,
            url: "https://eslint.org/docs/latest/rules/no-ex-assign"
        },

        schema: [],

        messages: {
            unexpected: "Do not assign to the exception parameter."
        }
    },

    create(context) {

        const sourceCode = context.sourceCode;

        /**
         * Finds and reports references that are non initializer and writable.
         * @param {Variable} variable A variable to check.
         * @returns {void}
         */
        function checkVariable(variable) {
            astUtils.getModifyingReferences(variable.references).forEach(reference => {
                context.report({ node: reference.identifier, messageId: "unexpected" });
            });
        }

        return {
            CatchClause(node) {
                sourceCode.getDeclaredVariables(node).forEach(checkVariable);
            }
        };

    }
};


code:
/**
 * @fileoverview Rule to flag adding properties to native object's prototypes.
 * @author David Nelson
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");
const globals = require("globals");

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow extending native types",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/no-extend-native"
        },

        schema: [
            {
                type: "object",
                properties: {
                    exceptions: {
                        type: "array",
                        items: {
                            type: "string"
                        },
                        uniqueItems: true
                    }
                },
                additionalProperties: false
            }
        ],

        messages: {
            unexpected: "{{builtin}} prototype is read only, properties should not be added."
        }
    },

    create(context) {

        const config = context.options[0] || {};
        const sourceCode = context.sourceCode;
        const exceptions = new Set(config.exceptions || []);
        const modifiedBuiltins = new Set(
            Object.keys(globals.builtin)
                .filter(builtin => builtin[0].toUpperCase() === builtin[0])
                .filter(builtin => !exceptions.has(builtin))
        );

        /**
         * Reports a lint error for the given node.
         * @param {ASTNode} node The node to report.
         * @param {string} builtin The name of the native builtin being extended.
         * @returns {void}
         */
        function reportNode(node, builtin) {
            context.report({
                node,
                messageId: "unexpected",
                data: {
                    builtin
                }
            });
        }

        /**
         * Check to see if the `prototype` property of the given object
         * identifier node is being accessed.
         * @param {ASTNode} identifierNode The Identifier representing the object
         * to check.
         * @returns {boolean} True if the identifier is the object of a
         * MemberExpression and its `prototype` property is being accessed,
         * false otherwise.
         */
        function isPrototypePropertyAccessed(identifierNode) {
            return Boolean(
                identifierNode &&
                identifierNode.parent &&
                identifierNode.parent.type === "MemberExpression" &&
                identifierNode.parent.object === identifierNode &&
                astUtils.getStaticPropertyName(identifierNode.parent) === "prototype"
            );
        }

        /**
         * Check if it's an assignment to the property of the given node.
         * Example: `*.prop = 0` // the `*` is the given node.
         * @param {ASTNode} node The node to check.
         * @returns {boolean} True if an assignment to the property of the node.
         */
        function isAssigningToPropertyOf(node) {
            return (
                node.parent.type === "MemberExpression" &&
                node.parent.object === node &&
                node.parent.parent.type === "AssignmentExpression" &&
                node.parent.parent.left === node.parent
            );
        }

        /**
         * Checks if the given node is at the first argument of the method call of `Object.defineProperty()` or `Object.defineProperties()`.
         * @param {ASTNode} node The node to check.
         * @returns {boolean} True if the node is at the first argument of the method call of `Object.defineProperty()` or `Object.defineProperties()`.
         */
        function isInDefinePropertyCall(node) {
            return (
                node.parent.type === "CallExpression" &&
                node.parent.arguments[0] === node &&
                astUtils.isSpecificMemberAccess(node.parent.callee, "Object", /^definePropert(?:y|ies)$/u)
            );
        }

        /**
         * Check to see if object prototype access is part of a prototype
         * extension. There are three ways a prototype can be extended:
         * 1. Assignment to prototype property (Object.prototype.foo = 1)
         * 2. Object.defineProperty()/Object.defineProperties() on a prototype
         * If prototype extension is detected, report the AssignmentExpression
         * or CallExpression node.
         * @param {ASTNode} identifierNode The Identifier representing the object
         * which prototype is being accessed and possibly extended.
         * @returns {void}
         */
        function checkAndReportPrototypeExtension(identifierNode) {
            if (!isPrototypePropertyAccessed(identifierNode)) {
                return; // This is not `*.prototype` access.
            }

            /*
             * `identifierNode.parent` is a MemberExpression `*.prototype`.
             * If it's an optional member access, it may be wrapped by a `ChainExpression` node.
             */
            const prototypeNode =
                identifierNode.parent.parent.type === "ChainExpression"
                    ? identifierNode.parent.parent
                    : identifierNode.parent;

            if (isAssigningToPropertyOf(prototypeNode)) {

                // `*.prototype` -> MemberExpression -> AssignmentExpression
                reportNode(prototypeNode.parent.parent, identifierNode.name);
            } else if (isInDefinePropertyCall(prototypeNode)) {

                // `*.prototype` -> CallExpression
                reportNode(prototypeNode.parent, identifierNode.name);
            }
        }

        return {

            "Program:exit"(node) {
                const globalScope = sourceCode.getScope(node);

                modifiedBuiltins.forEach(builtin => {
                    const builtinVar = globalScope.set.get(builtin);

                    if (builtinVar && builtinVar.references) {
                        builtinVar.references
                            .map(ref => ref.identifier)
                            .forEach(checkAndReportPrototypeExtension);
                    }
                });
            }
        };

    }
};


code:
/**
 * @fileoverview Rule to flag unnecessary bind calls
 * @author Bence Dányi <bence@danyi.me>
 */
"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

const SIDE_EFFECT_FREE_NODE_TYPES = new Set(["Literal", "Identifier", "ThisExpression", "FunctionExpression"]);

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow unnecessary calls to `.bind()`",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/no-extra-bind"
        },

        schema: [],
        fixable: "code",

        messages: {
            unexpected: "The function binding is unnecessary."
        }
    },

    create(context) {
        const sourceCode = context.sourceCode;
        let scopeInfo = null;

        /**
         * Checks if a node is free of side effects.
         *
         * This check is stricter than it needs to be, in order to keep the implementation simple.
         * @param {ASTNode} node A node to check.
         * @returns {boolean} True if the node is known to be side-effect free, false otherwise.
         */
        function isSideEffectFree(node) {
            return SIDE_EFFECT_FREE_NODE_TYPES.has(node.type);
        }

        /**
         * Reports a given function node.
         * @param {ASTNode} node A node to report. This is a FunctionExpression or
         *      an ArrowFunctionExpression.
         * @returns {void}
         */
        function report(node) {
            const memberNode = node.parent;
            const callNode = memberNode.parent.type === "ChainExpression"
                ? memberNode.parent.parent
                : memberNode.parent;

            context.report({
                node: callNode,
                messageId: "unexpected",
                loc: memberNode.property.loc,

                fix(fixer) {
                    if (!isSideEffectFree(callNode.arguments[0])) {
                        return null;
                    }

                    /*
                     * The list of the first/last token pair of a removal range.
                     * This is two parts because closing parentheses may exist between the method name and arguments.
                     * E.g. `(function(){}.bind ) (obj)`
                     *                    ^^^^^   ^^^^^ < removal ranges
                     * E.g. `(function(){}?.['bind'] ) ?.(obj)`
                     *                    ^^^^^^^^^^   ^^^^^^^ < removal ranges
                     */
                    const tokenPairs = [
                        [

                            // `.`, `?.`, or `[` token.
                            sourceCode.getTokenAfter(
                                memberNode.object,
                                astUtils.isNotClosingParenToken
                            ),

                            // property name or `]` token.
                            sourceCode.getLastToken(memberNode)
                        ],
                        [

                            // `?.` or `(` token of arguments.
                            sourceCode.getTokenAfter(
                                memberNode,
                                astUtils.isNotClosingParenToken
                            ),

                            // `)` token of arguments.
                            sourceCode.getLastToken(callNode)
                        ]
                    ];
                    const firstTokenToRemove = tokenPairs[0][0];
                    const lastTokenToRemove = tokenPairs[1][1];

                    if (sourceCode.commentsExistBetween(firstTokenToRemove, lastTokenToRemove)) {
                        return null;
                    }

                    return tokenPairs.map(([start, end]) =>
                        fixer.removeRange([start.range[0], end.range[1]]));
                }
            });
        }

        /**
         * Checks whether or not a given function node is the callee of `.bind()`
         * method.
         *
         * e.g. `(function() {}.bind(foo))`
         * @param {ASTNode} node A node to report. This is a FunctionExpression or
         *      an ArrowFunctionExpression.
         * @returns {boolean} `true` if the node is the callee of `.bind()` method.
         */
        function isCalleeOfBindMethod(node) {
            if (!astUtils.isSpecificMemberAccess(node.parent, null, "bind")) {
                return false;
            }

            // The node of `*.bind` member access.
            const bindNode = node.parent.parent.type === "ChainExpression"
                ? node.parent.parent
                : node.parent;

            return (
                bindNode.parent.type === "CallExpression" &&
                bindNode.parent.callee === bindNode &&
                bindNode.parent.arguments.length === 1 &&
                bindNode.parent.arguments[0].type !== "SpreadElement"
            );
        }

        /**
         * Adds a scope information object to the stack.
         * @param {ASTNode} node A node to add. This node is a FunctionExpression
         *      or a FunctionDeclaration node.
         * @returns {void}
         */
        function enterFunction(node) {
            scopeInfo = {
                isBound: isCalleeOfBindMethod(node),
                thisFound: false,
                upper: scopeInfo
            };
        }

        /**
         * Removes the scope information object from the top of the stack.
         * At the same time, this reports the function node if the function has
         * `.bind()` and the `this` keywords found.
         * @param {ASTNode} node A node to remove. This node is a
         *      FunctionExpression or a FunctionDeclaration node.
         * @returns {void}
         */
        function exitFunction(node) {
            if (scopeInfo.isBound && !scopeInfo.thisFound) {
                report(node);
            }

            scopeInfo = scopeInfo.upper;
        }

        /**
         * Reports a given arrow function if the function is callee of `.bind()`
         * method.
         * @param {ASTNode} node A node to report. This node is an
         *      ArrowFunctionExpression.
         * @returns {void}
         */
        function exitArrowFunction(node) {
            if (isCalleeOfBindMethod(node)) {
                report(node);
            }
        }

        /**
         * Set the mark as the `this` keyword was found in this scope.
         * @returns {void}
         */
        function markAsThisFound() {
            if (scopeInfo) {
                scopeInfo.thisFound = true;
            }
        }

        return {
            "ArrowFunctionExpression:exit": exitArrowFunction,
            FunctionDeclaration: enterFunction,
            "FunctionDeclaration:exit": exitFunction,
            FunctionExpression: enterFunction,
            "FunctionExpression:exit": exitFunction,
            ThisExpression: markAsThisFound
        };
    }
};


code:
/**
 * @fileoverview Rule to flag unnecessary double negation in Boolean contexts
 * @author Brandon Mills
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");
const eslintUtils = require("@eslint-community/eslint-utils");

const precedence = astUtils.getPrecedence;

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow unnecessary boolean casts",
            recommended: true,
            url: "https://eslint.org/docs/latest/rules/no-extra-boolean-cast"
        },

        schema: [{
            type: "object",
            properties: {
                enforceForLogicalOperands: {
                    type: "boolean",
                    default: false
                }
            },
            additionalProperties: false
        }],
        fixable: "code",

        messages: {
            unexpectedCall: "Redundant Boolean call.",
            unexpectedNegation: "Redundant double negation."
        }
    },

    create(context) {
        const sourceCode = context.sourceCode;

        // Node types which have a test which will coerce values to booleans.
        const BOOLEAN_NODE_TYPES = new Set([
            "IfStatement",
            "DoWhileStatement",
            "WhileStatement",
            "ConditionalExpression",
            "ForStatement"
        ]);

        /**
         * Check if a node is a Boolean function or constructor.
         * @param {ASTNode} node the node
         * @returns {boolean} If the node is Boolean function or constructor
         */
        function isBooleanFunctionOrConstructorCall(node) {

            // Boolean(<bool>) and new Boolean(<bool>)
            return (node.type === "CallExpression" || node.type === "NewExpression") &&
                    node.callee.type === "Identifier" &&
                        node.callee.name === "Boolean";
        }

        /**
         * Checks whether the node is a logical expression and that the option is enabled
         * @param {ASTNode} node the node
         * @returns {boolean} if the node is a logical expression and option is enabled
         */
        function isLogicalContext(node) {
            return node.type === "LogicalExpression" &&
            (node.operator === "||" || node.operator === "&&") &&
            (context.options.length && context.options[0].enforceForLogicalOperands === true);

        }

        /**
         * Check if a node is in a context where its value would be coerced to a boolean at runtime.
         * @param {ASTNode} node The node
         * @returns {boolean} If it is in a boolean context
         */
        function isInBooleanContext(node) {
            return (
                (isBooleanFunctionOrConstructorCall(node.parent) &&
                node === node.parent.arguments[0]) ||

                (BOOLEAN_NODE_TYPES.has(node.parent.type) &&
                    node === node.parent.test) ||

                // !<bool>
                (node.parent.type === "UnaryExpression" &&
                    node.parent.operator === "!")
            );
        }

        /**
         * Checks whether the node is a context that should report an error
         * Acts recursively if it is in a logical context
         * @param {ASTNode} node the node
         * @returns {boolean} If the node is in one of the flagged contexts
         */
        function isInFlaggedContext(node) {
            if (node.parent.type === "ChainExpression") {
                return isInFlaggedContext(node.parent);
            }

            return isInBooleanContext(node) ||
            (isLogicalContext(node.parent) &&

            // For nested logical statements
            isInFlaggedContext(node.parent)
            );
        }

        /**
         * Check if a node has comments inside.
         * @param {ASTNode} node The node to check.
         * @returns {boolean} `true` if it has comments inside.
         */
        function hasCommentsInside(node) {
            return Boolean(sourceCode.getCommentsInside(node).length);
        }

        /**
         * Checks if the given node is wrapped in grouping parentheses. Parentheses for constructs such as if() don't count.
         * @param {ASTNode} node The node to check.
         * @returns {boolean} `true` if the node is parenthesized.
         * @private
         */
        function isParenthesized(node) {
            return eslintUtils.isParenthesized(1, node, sourceCode);
        }

        /**
         * Determines whether the given node needs to be parenthesized when replacing the previous node.
         * It assumes that `previousNode` is the node to be reported by this rule, so it has a limited list
         * of possible parent node types. By the same assumption, the node's role in a particular parent is already known.
         * For example, if the parent is `ConditionalExpression`, `previousNode` must be its `test` child.
         * @param {ASTNode} previousNode Previous node.
         * @param {ASTNode} node The node to check.
         * @throws {Error} (Unreachable.)
         * @returns {boolean} `true` if the node needs to be parenthesized.
         */
        function needsParens(previousNode, node) {
            if (previousNode.parent.type === "ChainExpression") {
                return needsParens(previousNode.parent, node);
            }
            if (isParenthesized(previousNode)) {

                // parentheses around the previous node will stay, so there is no need for an additional pair
                return false;
            }

            // parent of the previous node will become parent of the replacement node
            const parent = previousNode.parent;

            switch (parent.type) {
                case "CallExpression":
                case "NewExpression":
                    return node.type === "SequenceExpression";
                case "IfStatement":
                case "DoWhileStatement":
                case "WhileStatement":
                case "ForStatement":
                    return false;
                case "ConditionalExpression":
                    return precedence(node) <= precedence(parent);
                case "UnaryExpression":
                    return precedence(node) < precedence(parent);
                case "LogicalExpression":
                    if (astUtils.isMixedLogicalAndCoalesceExpressions(node, parent)) {
                        return true;
                    }
                    if (previousNode === parent.left) {
                        return precedence(node) < precedence(parent);
                    }
                    return precedence(node) <= precedence(parent);

                /* c8 ignore next */
                default:
                    throw new Error(`Unexpected parent type: ${parent.type}`);
            }
        }

        return {
            UnaryExpression(node) {
                const parent = node.parent;

                // Exit early if it's guaranteed not to match
                if (node.operator !== "!" ||
                          parent.type !== "UnaryExpression" ||
                          parent.operator !== "!") {
                    return;
                }

                if (isInFlaggedContext(parent)) {
                    context.report({
                        node: parent,
                        messageId: "unexpectedNegation",
                        fix(fixer) {
                            if (hasCommentsInside(parent)) {
                                return null;
                            }

                            if (needsParens(parent, node.argument)) {
                                return fixer.replaceText(parent, `(${sourceCode.getText(node.argument)})`);
                            }

                            let prefix = "";
                            const tokenBefore = sourceCode.getTokenBefore(parent);
                            const firstReplacementToken = sourceCode.getFirstToken(node.argument);

                            if (
                                tokenBefore &&
                                tokenBefore.range[1] === parent.range[0] &&
                                !astUtils.canTokensBeAdjacent(tokenBefore, firstReplacementToken)
                            ) {
                                prefix = " ";
                            }

                            return fixer.replaceText(parent, prefix + sourceCode.getText(node.argument));
                        }
                    });
                }
            },

            CallExpression(node) {
                if (node.callee.type !== "Identifier" || node.callee.name !== "Boolean") {
                    return;
                }

                if (isInFlaggedContext(node)) {
                    context.report({
                        node,
                        messageId: "unexpectedCall",
                        fix(fixer) {
                            const parent = node.parent;

                            if (node.arguments.length === 0) {
                                if (parent.type === "UnaryExpression" && parent.operator === "!") {

                                    /*
                                     * !Boolean() -> true
                                     */

                                    if (hasCommentsInside(parent)) {
                                        return null;
                                    }

                                    const replacement = "true";
                                    let prefix = "";
                                    const tokenBefore = sourceCode.getTokenBefore(parent);

                                    if (
                                        tokenBefore &&
                                        tokenBefore.range[1] === parent.range[0] &&
                                        !astUtils.canTokensBeAdjacent(tokenBefore, replacement)
                                    ) {
                                        prefix = " ";
                                    }

                                    return fixer.replaceText(parent, prefix + replacement);
                                }

                                /*
                                 * Boolean() -> false
                                 */

                                if (hasCommentsInside(node)) {
                                    return null;
                                }

                                return fixer.replaceText(node, "false");
                            }

                            if (node.arguments.length === 1) {
                                const argument = node.arguments[0];

                                if (argument.type === "SpreadElement" || hasCommentsInside(node)) {
                                    return null;
                                }

                                /*
                                 * Boolean(expression) -> expression
                                 */

                                if (needsParens(node, argument)) {
                                    return fixer.replaceText(node, `(${sourceCode.getText(argument)})`);
                                }

                                return fixer.replaceText(node, sourceCode.getText(argument));
                            }

                            // two or more arguments
                            return null;
                        }
                    });
                }
            }
        };

    }
};


code:
/**
 * @fileoverview Rule to disallow unnecessary labels
 * @author Toru Nagashima
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow unnecessary labels",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/no-extra-label"
        },

        schema: [],
        fixable: "code",

        messages: {
            unexpected: "This label '{{name}}' is unnecessary."
        }
    },

    create(context) {
        const sourceCode = context.sourceCode;
        let scopeInfo = null;

        /**
         * Creates a new scope with a breakable statement.
         * @param {ASTNode} node A node to create. This is a BreakableStatement.
         * @returns {void}
         */
        function enterBreakableStatement(node) {
            scopeInfo = {
                label: node.parent.type === "LabeledStatement" ? node.parent.label : null,
                breakable: true,
                upper: scopeInfo
            };
        }

        /**
         * Removes the top scope of the stack.
         * @returns {void}
         */
        function exitBreakableStatement() {
            scopeInfo = scopeInfo.upper;
        }

        /**
         * Creates a new scope with a labeled statement.
         *
         * This ignores it if the body is a breakable statement.
         * In this case it's handled in the `enterBreakableStatement` function.
         * @param {ASTNode} node A node to create. This is a LabeledStatement.
         * @returns {void}
         */
        function enterLabeledStatement(node) {
            if (!astUtils.isBreakableStatement(node.body)) {
                scopeInfo = {
                    label: node.label,
                    breakable: false,
                    upper: scopeInfo
                };
            }
        }

        /**
         * Removes the top scope of the stack.
         *
         * This ignores it if the body is a breakable statement.
         * In this case it's handled in the `exitBreakableStatement` function.
         * @param {ASTNode} node A node. This is a LabeledStatement.
         * @returns {void}
         */
        function exitLabeledStatement(node) {
            if (!astUtils.isBreakableStatement(node.body)) {
                scopeInfo = scopeInfo.upper;
            }
        }

        /**
         * Reports a given control node if it's unnecessary.
         * @param {ASTNode} node A node. This is a BreakStatement or a
         *      ContinueStatement.
         * @returns {void}
         */
        function reportIfUnnecessary(node) {
            if (!node.label) {
                return;
            }

            const labelNode = node.label;

            for (let info = scopeInfo; info !== null; info = info.upper) {
                if (info.breakable || info.label && info.label.name === labelNode.name) {
                    if (info.breakable && info.label && info.label.name === labelNode.name) {
                        context.report({
                            node: labelNode,
                            messageId: "unexpected",
                            data: labelNode,
                            fix(fixer) {
                                const breakOrContinueToken = sourceCode.getFirstToken(node);

                                if (sourceCode.commentsExistBetween(breakOrContinueToken, labelNode)) {
                                    return null;
                                }

                                return fixer.removeRange([breakOrContinueToken.range[1], labelNode.range[1]]);
                            }
                        });
                    }
                    return;
                }
            }
        }

        return {
            WhileStatement: enterBreakableStatement,
            "WhileStatement:exit": exitBreakableStatement,
            DoWhileStatement: enterBreakableStatement,
            "DoWhileStatement:exit": exitBreakableStatement,
            ForStatement: enterBreakableStatement,
            "ForStatement:exit": exitBreakableStatement,
            ForInStatement: enterBreakableStatement,
            "ForInStatement:exit": exitBreakableStatement,
            ForOfStatement: enterBreakableStatement,
            "ForOfStatement:exit": exitBreakableStatement,
            SwitchStatement: enterBreakableStatement,
            "SwitchStatement:exit": exitBreakableStatement,
            LabeledStatement: enterLabeledStatement,
            "LabeledStatement:exit": exitLabeledStatement,
            BreakStatement: reportIfUnnecessary,
            ContinueStatement: reportIfUnnecessary
        };
    }
};