code:
/**
 * @fileoverview Ensure handling of errors when we know they exist.
 * @author Jamund Ferguson
 * @deprecated in ESLint v7.0.0
 */

"use strict";

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

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

        replacedBy: [],

        type: "suggestion",

        docs: {
            description: "Require error handling in callbacks",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/handle-callback-err"
        },

        schema: [
            {
                type: "string"
            }
        ],
        messages: {
            expected: "Expected error to be handled."
        }
    },

    create(context) {

        const errorArgument = context.options[0] || "err";
        const sourceCode = context.sourceCode;

        /**
         * Checks if the given argument should be interpreted as a regexp pattern.
         * @param {string} stringToCheck The string which should be checked.
         * @returns {boolean} Whether or not the string should be interpreted as a pattern.
         */
        function isPattern(stringToCheck) {
            const firstChar = stringToCheck[0];

            return firstChar === "^";
        }

        /**
         * Checks if the given name matches the configured error argument.
         * @param {string} name The name which should be compared.
         * @returns {boolean} Whether or not the given name matches the configured error variable name.
         */
        function matchesConfiguredErrorName(name) {
            if (isPattern(errorArgument)) {
                const regexp = new RegExp(errorArgument, "u");

                return regexp.test(name);
            }
            return name === errorArgument;
        }

        /**
         * Get the parameters of a given function scope.
         * @param {Object} scope The function scope.
         * @returns {Array} All parameters of the given scope.
         */
        function getParameters(scope) {
            return scope.variables.filter(variable => variable.defs[0] && variable.defs[0].type === "Parameter");
        }

        /**
         * Check to see if we're handling the error object properly.
         * @param {ASTNode} node The AST node to check.
         * @returns {void}
         */
        function checkForError(node) {
            const scope = sourceCode.getScope(node),
                parameters = getParameters(scope),
                firstParameter = parameters[0];

            if (firstParameter && matchesConfiguredErrorName(firstParameter.name)) {
                if (firstParameter.references.length === 0) {
                    context.report({ node, messageId: "expected" });
                }
            }
        }

        return {
            FunctionDeclaration: checkForError,
            FunctionExpression: checkForError,
            ArrowFunctionExpression: checkForError
        };

    }
};


code:
/**
 * @fileoverview Rule that warns when identifier names that are
 * specified in the configuration are used.
 * @author Keith Cirkel (http://keithcirkel.co.uk)
 * @deprecated in ESLint v7.5.0
 */

"use strict";

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

/**
 * Checks whether the given node represents assignment target in a normal assignment or destructuring.
 * @param {ASTNode} node The node to check.
 * @returns {boolean} `true` if the node is assignment target.
 */
function isAssignmentTarget(node) {
    const parent = node.parent;

    return (

        // normal assignment
        (
            parent.type === "AssignmentExpression" &&
            parent.left === node
        ) ||

        // destructuring
        parent.type === "ArrayPattern" ||
        parent.type === "RestElement" ||
        (
            parent.type === "Property" &&
            parent.value === node &&
            parent.parent.type === "ObjectPattern"
        ) ||
        (
            parent.type === "AssignmentPattern" &&
            parent.left === node
        )
    );
}

/**
 * Checks whether the given node represents an imported name that is renamed in the same import/export specifier.
 *
 * Examples:
 * import { a as b } from 'mod'; // node `a` is renamed import
 * export { a as b } from 'mod'; // node `a` is renamed import
 * @param {ASTNode} node `Identifier` node to check.
 * @returns {boolean} `true` if the node is a renamed import.
 */
function isRenamedImport(node) {
    const parent = node.parent;

    return (
        (
            parent.type === "ImportSpecifier" &&
            parent.imported !== parent.local &&
            parent.imported === node
        ) ||
        (
            parent.type === "ExportSpecifier" &&
            parent.parent.source && // re-export
            parent.local !== parent.exported &&
            parent.local === node
        )
    );
}

/**
 * Checks whether the given node is a renamed identifier node in an ObjectPattern destructuring.
 *
 * Examples:
 * const { a : b } = foo; // node `a` is renamed node.
 * @param {ASTNode} node `Identifier` node to check.
 * @returns {boolean} `true` if the node is a renamed node in an ObjectPattern destructuring.
 */
function isRenamedInDestructuring(node) {
    const parent = node.parent;

    return (
        (
            !parent.computed &&
            parent.type === "Property" &&
            parent.parent.type === "ObjectPattern" &&
            parent.value !== node &&
            parent.key === node
        )
    );
}

/**
 * Checks whether the given node represents shorthand definition of a property in an object literal.
 * @param {ASTNode} node `Identifier` node to check.
 * @returns {boolean} `true` if the node is a shorthand property definition.
 */
function isShorthandPropertyDefinition(node) {
    const parent = node.parent;

    return (
        parent.type === "Property" &&
        parent.parent.type === "ObjectExpression" &&
        parent.shorthand
    );
}

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

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        deprecated: true,
        replacedBy: ["id-denylist"],

        type: "suggestion",

        docs: {
            description: "Disallow specified identifiers",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/id-blacklist"
        },

        schema: {
            type: "array",
            items: {
                type: "string"
            },
            uniqueItems: true
        },
        messages: {
            restricted: "Identifier '{{name}}' is restricted."
        }
    },

    create(context) {

        const denyList = new Set(context.options);
        const reportedNodes = new Set();
        const sourceCode = context.sourceCode;

        let globalScope;

        /**
         * Checks whether the given name is restricted.
         * @param {string} name The name to check.
         * @returns {boolean} `true` if the name is restricted.
         * @private
         */
        function isRestricted(name) {
            return denyList.has(name);
        }

        /**
         * Checks whether the given node represents a reference to a global variable that is not declared in the source code.
         * These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
         * @param {ASTNode} node `Identifier` node to check.
         * @returns {boolean} `true` if the node is a reference to a global variable.
         */
        function isReferenceToGlobalVariable(node) {
            const variable = globalScope.set.get(node.name);

            return variable && variable.defs.length === 0 &&
                variable.references.some(ref => ref.identifier === node);
        }

        /**
         * Determines whether the given node should be checked.
         * @param {ASTNode} node `Identifier` node.
         * @returns {boolean} `true` if the node should be checked.
         */
        function shouldCheck(node) {
            const parent = node.parent;

            /*
             * Member access has special rules for checking property names.
             * Read access to a property with a restricted name is allowed, because it can be on an object that user has no control over.
             * Write access isn't allowed, because it potentially creates a new property with a restricted name.
             */
            if (
                parent.type === "MemberExpression" &&
                parent.property === node &&
                !parent.computed
            ) {
                return isAssignmentTarget(parent);
            }

            return (
                parent.type !== "CallExpression" &&
                parent.type !== "NewExpression" &&
                !isRenamedImport(node) &&
                !isRenamedInDestructuring(node) &&
                !(
                    isReferenceToGlobalVariable(node) &&
                    !isShorthandPropertyDefinition(node)
                )
            );
        }

        /**
         * Reports an AST node as a rule violation.
         * @param {ASTNode} node The node to report.
         * @returns {void}
         * @private
         */
        function report(node) {

            /*
             * We used the range instead of the node because it's possible
             * for the same identifier to be represented by two different
             * nodes, with the most clear example being shorthand properties:
             * { foo }
             * In this case, "foo" is represented by one node for the name
             * and one for the value. The only way to know they are the same
             * is to look at the range.
             */
            if (!reportedNodes.has(node.range.toString())) {
                context.report({
                    node,
                    messageId: "restricted",
                    data: {
                        name: node.name
                    }
                });
                reportedNodes.add(node.range.toString());
            }

        }

        return {

            Program(node) {
                globalScope = sourceCode.getScope(node);
            },

            Identifier(node) {
                if (isRestricted(node.name) && shouldCheck(node)) {
                    report(node);
                }
            }
        };
    }
};

license: mit


code:
/**
 * @fileoverview Rule that warns when identifier names that are
 * specified in the configuration are used.
 * @author Keith Cirkel (http://keithcirkel.co.uk)
 */

"use strict";

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

/**
 * Checks whether the given node represents assignment target in a normal assignment or destructuring.
 * @param {ASTNode} node The node to check.
 * @returns {boolean} `true` if the node is assignment target.
 */
function isAssignmentTarget(node) {
    const parent = node.parent;

    return (

        // normal assignment
        (
            parent.type === "AssignmentExpression" &&
            parent.left === node
        ) ||

        // destructuring
        parent.type === "ArrayPattern" ||
        parent.type === "RestElement" ||
        (
            parent.type === "Property" &&
            parent.value === node &&
            parent.parent.type === "ObjectPattern"
        ) ||
        (
            parent.type === "AssignmentPattern" &&
            parent.left === node
        )
    );
}

/**
 * Checks whether the given node represents an imported name that is renamed in the same import/export specifier.
 *
 * Examples:
 * import { a as b } from 'mod'; // node `a` is renamed import
 * export { a as b } from 'mod'; // node `a` is renamed import
 * @param {ASTNode} node `Identifier` node to check.
 * @returns {boolean} `true` if the node is a renamed import.
 */
function isRenamedImport(node) {
    const parent = node.parent;

    return (
        (
            parent.type === "ImportSpecifier" &&
            parent.imported !== parent.local &&
            parent.imported === node
        ) ||
        (
            parent.type === "ExportSpecifier" &&
            parent.parent.source && // re-export
            parent.local !== parent.exported &&
            parent.local === node
        )
    );
}

/**
 * Checks whether the given node is an ObjectPattern destructuring.
 *
 * Examples:
 * const { a : b } = foo;
 * @param {ASTNode} node `Identifier` node to check.
 * @returns {boolean} `true` if the node is in an ObjectPattern destructuring.
 */
function isPropertyNameInDestructuring(node) {
    const parent = node.parent;

    return (
        (
            !parent.computed &&
            parent.type === "Property" &&
            parent.parent.type === "ObjectPattern" &&
            parent.key === node
        )
    );
}

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

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

        docs: {
            description: "Disallow specified identifiers",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/id-denylist"
        },

        schema: {
            type: "array",
            items: {
                type: "string"
            },
            uniqueItems: true
        },
        messages: {
            restricted: "Identifier '{{name}}' is restricted.",
            restrictedPrivate: "Identifier '#{{name}}' is restricted."
        }
    },

    create(context) {

        const denyList = new Set(context.options);
        const reportedNodes = new Set();
        const sourceCode = context.sourceCode;

        let globalScope;

        /**
         * Checks whether the given name is restricted.
         * @param {string} name The name to check.
         * @returns {boolean} `true` if the name is restricted.
         * @private
         */
        function isRestricted(name) {
            return denyList.has(name);
        }

        /**
         * Checks whether the given node represents a reference to a global variable that is not declared in the source code.
         * These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
         * @param {ASTNode} node `Identifier` node to check.
         * @returns {boolean} `true` if the node is a reference to a global variable.
         */
        function isReferenceToGlobalVariable(node) {
            const variable = globalScope.set.get(node.name);

            return variable && variable.defs.length === 0 &&
                variable.references.some(ref => ref.identifier === node);
        }

        /**
         * Determines whether the given node should be checked.
         * @param {ASTNode} node `Identifier` node.
         * @returns {boolean} `true` if the node should be checked.
         */
        function shouldCheck(node) {
            const parent = node.parent;

            /*
             * Member access has special rules for checking property names.
             * Read access to a property with a restricted name is allowed, because it can be on an object that user has no control over.
             * Write access isn't allowed, because it potentially creates a new property with a restricted name.
             */
            if (
                parent.type === "MemberExpression" &&
                parent.property === node &&
                !parent.computed
            ) {
                return isAssignmentTarget(parent);
            }

            return (
                parent.type !== "CallExpression" &&
                parent.type !== "NewExpression" &&
                !isRenamedImport(node) &&
                !isPropertyNameInDestructuring(node) &&
                !isReferenceToGlobalVariable(node)
            );
        }

        /**
         * Reports an AST node as a rule violation.
         * @param {ASTNode} node The node to report.
         * @returns {void}
         * @private
         */
        function report(node) {

            /*
             * We used the range instead of the node because it's possible
             * for the same identifier to be represented by two different
             * nodes, with the most clear example being shorthand properties:
             * { foo }
             * In this case, "foo" is represented by one node for the name
             * and one for the value. The only way to know they are the same
             * is to look at the range.
             */
            if (!reportedNodes.has(node.range.toString())) {
                const isPrivate = node.type === "PrivateIdentifier";

                context.report({
                    node,
                    messageId: isPrivate ? "restrictedPrivate" : "restricted",
                    data: {
                        name: node.name
                    }
                });
                reportedNodes.add(node.range.toString());
            }
        }

        return {

            Program(node) {
                globalScope = sourceCode.getScope(node);
            },

            [[
                "Identifier",
                "PrivateIdentifier"
            ]](node) {
                if (isRestricted(node.name) && shouldCheck(node)) {
                    report(node);
                }
            }
        };
    }
};


code:
/**
 * @fileoverview Rule that warns when identifier names are shorter or longer
 * than the values provided in configuration.
 * @author Burak Yigit Kaya aka BYK
 */

"use strict";

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

const { getGraphemeCount } = require("../shared/string-utils");

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

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

        docs: {
            description: "Enforce minimum and maximum identifier lengths",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/id-length"
        },

        schema: [
            {
                type: "object",
                properties: {
                    min: {
                        type: "integer",
                        default: 2
                    },
                    max: {
                        type: "integer"
                    },
                    exceptions: {
                        type: "array",
                        uniqueItems: true,
                        items: {
                            type: "string"
                        }
                    },
                    exceptionPatterns: {
                        type: "array",
                        uniqueItems: true,
                        items: {
                            type: "string"
                        }
                    },
                    properties: {
                        enum: ["always", "never"]
                    }
                },
                additionalProperties: false
            }
        ],
        messages: {
            tooShort: "Identifier name '{{name}}' is too short (< {{min}}).",
            tooShortPrivate: "Identifier name '#{{name}}' is too short (< {{min}}).",
            tooLong: "Identifier name '{{name}}' is too long (> {{max}}).",
            tooLongPrivate: "Identifier name #'{{name}}' is too long (> {{max}})."
        }
    },

    create(context) {
        const options = context.options[0] || {};
        const minLength = typeof options.min !== "undefined" ? options.min : 2;
        const maxLength = typeof options.max !== "undefined" ? options.max : Infinity;
        const properties = options.properties !== "never";
        const exceptions = new Set(options.exceptions);
        const exceptionPatterns = (options.exceptionPatterns || []).map(pattern => new RegExp(pattern, "u"));
        const reportedNodes = new Set();

        /**
         * Checks if a string matches the provided exception patterns
         * @param {string} name The string to check.
         * @returns {boolean} if the string is a match
         * @private
         */
        function matchesExceptionPattern(name) {
            return exceptionPatterns.some(pattern => pattern.test(name));
        }

        const SUPPORTED_EXPRESSIONS = {
            MemberExpression: properties && function(parent) {
                return !parent.computed && (

                    // regular property assignment
                    (parent.parent.left === parent && parent.parent.type === "AssignmentExpression" ||

                    // or the last identifier in an ObjectPattern destructuring
                    parent.parent.type === "Property" && parent.parent.value === parent &&
                    parent.parent.parent.type === "ObjectPattern" && parent.parent.parent.parent.left === parent.parent.parent)
                );
            },
            AssignmentPattern(parent, node) {
                return parent.left === node;
            },
            VariableDeclarator(parent, node) {
                return parent.id === node;
            },
            Property(parent, node) {

                if (parent.parent.type === "ObjectPattern") {
                    const isKeyAndValueSame = parent.value.name === parent.key.name;

                    return (
                        !isKeyAndValueSame && parent.value === node ||
                        isKeyAndValueSame && parent.key === node && properties
                    );
                }
                return properties && !parent.computed && parent.key.name === node.name;
            },
            ImportDefaultSpecifier: true,
            RestElement: true,
            FunctionExpression: true,
            ArrowFunctionExpression: true,
            ClassDeclaration: true,
            FunctionDeclaration: true,
            MethodDefinition: true,
            PropertyDefinition: true,
            CatchClause: true,
            ArrayPattern: true
        };

        return {
            [[
                "Identifier",
                "PrivateIdentifier"
            ]](node) {
                const name = node.name;
                const parent = node.parent;

                const nameLength = getGraphemeCount(name);

                const isShort = nameLength < minLength;
                const isLong = nameLength > maxLength;

                if (!(isShort || isLong) || exceptions.has(name) || matchesExceptionPattern(name)) {
                    return; // Nothing to report
                }

                const isValidExpression = SUPPORTED_EXPRESSIONS[parent.type];

                /*
                 * We used the range instead of the node because it's possible
                 * for the same identifier to be represented by two different
                 * nodes, with the most clear example being shorthand properties:
                 * { foo }
                 * In this case, "foo" is represented by one node for the name
                 * and one for the value. The only way to know they are the same
                 * is to look at the range.
                 */
                if (isValidExpression && !reportedNodes.has(node.range.toString()) && (isValidExpression === true || isValidExpression(parent, node))) {
                    reportedNodes.add(node.range.toString());

                    let messageId = isShort ? "tooShort" : "tooLong";

                    if (node.type === "PrivateIdentifier") {
                        messageId += "Private";
                    }

                    context.report({
                        node,
                        messageId,
                        data: { name, min: minLength, max: maxLength }
                    });
                }
            }
        };
    }
};


code:
/**
 * @fileoverview Rule to flag non-matching identifiers
 * @author Matthieu Larcher
 */

"use strict";

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

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

        docs: {
            description: "Require identifiers to match a specified regular expression",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/id-match"
        },

        schema: [
            {
                type: "string"
            },
            {
                type: "object",
                properties: {
                    properties: {
                        type: "boolean",
                        default: false
                    },
                    classFields: {
                        type: "boolean",
                        default: false
                    },
                    onlyDeclarations: {
                        type: "boolean",
                        default: false
                    },
                    ignoreDestructuring: {
                        type: "boolean",
                        default: false
                    }
                },
                additionalProperties: false
            }
        ],
        messages: {
            notMatch: "Identifier '{{name}}' does not match the pattern '{{pattern}}'.",
            notMatchPrivate: "Identifier '#{{name}}' does not match the pattern '{{pattern}}'."
        }
    },

    create(context) {

        //--------------------------------------------------------------------------
        // Options
        //--------------------------------------------------------------------------
        const pattern = context.options[0] || "^.+$",
            regexp = new RegExp(pattern, "u");

        const options = context.options[1] || {},
            checkProperties = !!options.properties,
            checkClassFields = !!options.classFields,
            onlyDeclarations = !!options.onlyDeclarations,
            ignoreDestructuring = !!options.ignoreDestructuring;

        const sourceCode = context.sourceCode;
        let globalScope;

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

        // contains reported nodes to avoid reporting twice on destructuring with shorthand notation
        const reportedNodes = new Set();
        const ALLOWED_PARENT_TYPES = new Set(["CallExpression", "NewExpression"]);
        const DECLARATION_TYPES = new Set(["FunctionDeclaration", "VariableDeclarator"]);
        const IMPORT_TYPES = new Set(["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"]);

        /**
         * Checks whether the given node represents a reference to a global variable that is not declared in the source code.
         * These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
         * @param {ASTNode} node `Identifier` node to check.
         * @returns {boolean} `true` if the node is a reference to a global variable.
         */
        function isReferenceToGlobalVariable(node) {
            const variable = globalScope.set.get(node.name);

            return variable && variable.defs.length === 0 &&
                variable.references.some(ref => ref.identifier === node);
        }

        /**
         * Checks if a string matches the provided pattern
         * @param {string} name The string to check.
         * @returns {boolean} if the string is a match
         * @private
         */
        function isInvalid(name) {
            return !regexp.test(name);
        }

        /**
         * Checks if a parent of a node is an ObjectPattern.
         * @param {ASTNode} node The node to check.
         * @returns {boolean} if the node is inside an ObjectPattern
         * @private
         */
        function isInsideObjectPattern(node) {
            let { parent } = node;

            while (parent) {
                if (parent.type === "ObjectPattern") {
                    return true;
                }

                parent = parent.parent;
            }

            return false;
        }

        /**
         * Verifies if we should report an error or not based on the effective
         * parent node and the identifier name.
         * @param {ASTNode} effectiveParent The effective parent node of the node to be reported
         * @param {string} name The identifier name of the identifier node
         * @returns {boolean} whether an error should be reported or not
         */
        function shouldReport(effectiveParent, name) {
            return (!onlyDeclarations || DECLARATION_TYPES.has(effectiveParent.type)) &&
                !ALLOWED_PARENT_TYPES.has(effectiveParent.type) && isInvalid(name);
        }

        /**
         * Reports an AST node as a rule violation.
         * @param {ASTNode} node The node to report.
         * @returns {void}
         * @private
         */
        function report(node) {

            /*
             * We used the range instead of the node because it's possible
             * for the same identifier to be represented by two different
             * nodes, with the most clear example being shorthand properties:
             * { foo }
             * In this case, "foo" is represented by one node for the name
             * and one for the value. The only way to know they are the same
             * is to look at the range.
             */
            if (!reportedNodes.has(node.range.toString())) {

                const messageId = (node.type === "PrivateIdentifier")
                    ? "notMatchPrivate" : "notMatch";

                context.report({
                    node,
                    messageId,
                    data: {
                        name: node.name,
                        pattern
                    }
                });
                reportedNodes.add(node.range.toString());
            }
        }

        return {

            Program(node) {
                globalScope = sourceCode.getScope(node);
            },

            Identifier(node) {
                const name = node.name,
                    parent = node.parent,
                    effectiveParent = (parent.type === "MemberExpression") ? parent.parent : parent;

                if (isReferenceToGlobalVariable(node)) {
                    return;
                }

                if (parent.type === "MemberExpression") {

                    if (!checkProperties) {
                        return;
                    }

                    // Always check object names
                    if (parent.object.type === "Identifier" &&
                        parent.object.name === name) {
                        if (isInvalid(name)) {
                            report(node);
                        }

                    // Report AssignmentExpressions left side's assigned variable id
                    } else if (effectiveParent.type === "AssignmentExpression" &&
                        effectiveParent.left.type === "MemberExpression" &&
                        effectiveParent.left.property.name === node.name) {
                        if (isInvalid(name)) {
                            report(node);
                        }

                    // Report AssignmentExpressions only if they are the left side of the assignment
                    } else if (effectiveParent.type === "AssignmentExpression" && effectiveParent.right.type !== "MemberExpression") {
                        if (isInvalid(name)) {
                            report(node);
                        }
                    }

                // For https://github.com/eslint/eslint/issues/15123
                } else if (
                    parent.type === "Property" &&
                    parent.parent.type === "ObjectExpression" &&
                    parent.key === node &&
                    !parent.computed
                ) {
                    if (checkProperties && isInvalid(name)) {
                        report(node);
                    }

                /*
                 * Properties have their own rules, and
                 * AssignmentPattern nodes can be treated like Properties:
                 * e.g.: const { no_camelcased = false } = bar;
                 */
                } else if (parent.type === "Property" || parent.type === "AssignmentPattern") {

                    if (parent.parent && parent.parent.type === "ObjectPattern") {
                        if (!ignoreDestructuring && parent.shorthand && parent.value.left && isInvalid(name)) {
                            report(node);
                        }

                        const assignmentKeyEqualsValue = parent.key.name === parent.value.name;

                        // prevent checking righthand side of destructured object
                        if (!assignmentKeyEqualsValue && parent.key === node) {
                            return;
                        }

                        const valueIsInvalid = parent.value.name && isInvalid(name);

                        // ignore destructuring if the option is set, unless a new identifier is created
                        if (valueIsInvalid && !(assignmentKeyEqualsValue && ignoreDestructuring)) {
                            report(node);
                        }
                    }

                    // never check properties or always ignore destructuring
                    if ((!checkProperties && !parent.computed) || (ignoreDestructuring && isInsideObjectPattern(node))) {
                        return;
                    }

                    // don't check right hand side of AssignmentExpression to prevent duplicate warnings
                    if (parent.right !== node && shouldReport(effectiveParent, name)) {
                        report(node);
                    }

                // Check if it's an import specifier
                } else if (IMPORT_TYPES.has(parent.type)) {

                    // Report only if the local imported identifier is invalid
                    if (parent.local && parent.local.name === node.name && isInvalid(name)) {
                        report(node);
                    }

                } else if (parent.type === "PropertyDefinition") {

                    if (checkClassFields && isInvalid(name)) {
                        report(node);
                    }

                // Report anything that is invalid that isn't a CallExpression
                } else if (shouldReport(effectiveParent, name)) {
                    report(node);
                }
            },

            "PrivateIdentifier"(node) {

                const isClassField = node.parent.type === "PropertyDefinition";

                if (isClassField && !checkClassFields) {
                    return;
                }

                if (isInvalid(node.name)) {
                    report(node);
                }
            }

        };

    }
};


code:
/**
 * @fileoverview enforce the location of arrow function bodies
 * @author Sharmila Jesupaul
 * @deprecated in ESLint v8.53.0
 */
"use strict";

const { isCommentToken, isNotOpeningParenToken } = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        deprecated: true,
        replacedBy: [],
        type: "layout",

        docs: {
            description: "Enforce the location of arrow function bodies",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/implicit-arrow-linebreak"
        },

        fixable: "whitespace",

        schema: [
            {
                enum: ["beside", "below"]
            }
        ],
        messages: {
            expected: "Expected a linebreak before this expression.",
            unexpected: "Expected no linebreak before this expression."
        }
    },

    create(context) {
        const sourceCode = context.sourceCode;
        const option = context.options[0] || "beside";

        /**
         * Validates the location of an arrow function body
         * @param {ASTNode} node The arrow function body
         * @returns {void}
         */
        function validateExpression(node) {
            if (node.body.type === "BlockStatement") {
                return;
            }

            const arrowToken = sourceCode.getTokenBefore(node.body, isNotOpeningParenToken);
            const firstTokenOfBody = sourceCode.getTokenAfter(arrowToken);

            if (arrowToken.loc.end.line === firstTokenOfBody.loc.start.line && option === "below") {
                context.report({
                    node: firstTokenOfBody,
                    messageId: "expected",
                    fix: fixer => fixer.insertTextBefore(firstTokenOfBody, "\n")
                });
            } else if (arrowToken.loc.end.line !== firstTokenOfBody.loc.start.line && option === "beside") {
                context.report({
                    node: firstTokenOfBody,
                    messageId: "unexpected",
                    fix(fixer) {
                        if (sourceCode.getFirstTokenBetween(arrowToken, firstTokenOfBody, { includeComments: true, filter: isCommentToken })) {
                            return null;
                        }

                        return fixer.replaceTextRange([arrowToken.range[1], firstTokenOfBody.range[0]], " ");
                    }
                });
            }
        }

        //----------------------------------------------------------------------
        // Public
        //----------------------------------------------------------------------
        return {
            ArrowFunctionExpression: node => validateExpression(node)
        };
    }
};


code:
/**
 * @fileoverview This option sets a specific tab width for your code
 *
 * This rule has been ported and modified from nodeca.
 * @author Vitaly Puzrin
 * @author Gyandeep Singh
 * @deprecated in ESLint v4.0.0
 */

"use strict";

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

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

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
// this rule has known coverage issues, but it's deprecated and shouldn't be updated in the future anyway.
/* c8 ignore next */
/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "layout",

        docs: {
            description: "Enforce consistent indentation",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/indent-legacy"
        },

        deprecated: true,

        replacedBy: ["indent"],

        fixable: "whitespace",

        schema: [
            {
                oneOf: [
                    {
                        enum: ["tab"]
                    },
                    {
                        type: "integer",
                        minimum: 0
                    }
                ]
            },
            {
                type: "object",
                properties: {
                    SwitchCase: {
                        type: "integer",
                        minimum: 0
                    },
                    VariableDeclarator: {
                        oneOf: [
                            {
                                type: "integer",
                                minimum: 0
                            },
                            {
                                type: "object",
                                properties: {
                                    var: {
                                        type: "integer",
                                        minimum: 0
                                    },
                                    let: {
                                        type: "integer",
                                        minimum: 0
                                    },
                                    const: {
                                        type: "integer",
                                        minimum: 0
                                    }
                                }
                            }
                        ]
                    },
                    outerIIFEBody: {
                        type: "integer",
                        minimum: 0
                    },
                    MemberExpression: {
                        type: "integer",
                        minimum: 0
                    },
                    FunctionDeclaration: {
                        type: "object",
                        properties: {
                            parameters: {
                                oneOf: [
                                    {
                                        type: "integer",
                                        minimum: 0
                                    },
                                    {
                                        enum: ["first"]
                                    }
                                ]
                            },
                            body: {
                                type: "integer",
                                minimum: 0
                            }
                        }
                    },
                    FunctionExpression: {
                        type: "object",
                        properties: {
                            parameters: {
                                oneOf: [
                                    {
                                        type: "integer",
                                        minimum: 0
                                    },
                                    {
                                        enum: ["first"]
                                    }
                                ]
                            },
                            body: {
                                type: "integer",
                                minimum: 0
                            }
                        }
                    },
                    CallExpression: {
                        type: "object",
                        properties: {
                            parameters: {
                                oneOf: [
                                    {
                                        type: "integer",
                                        minimum: 0
                                    },
                                    {
                                        enum: ["first"]
                                    }
                                ]
                            }
                        }
                    },
                    ArrayExpression: {
                        oneOf: [
                            {
                                type: "integer",
                                minimum: 0
                            },
                            {
                                enum: ["first"]
                            }
                        ]
                    },
                    ObjectExpression: {
                        oneOf: [
                            {
                                type: "integer",
                                minimum: 0
                            },
                            {
                                enum: ["first"]
                            }
                        ]
                    }
                },
                additionalProperties: false
            }
        ],
        messages: {
            expected: "Expected indentation of {{expected}} but found {{actual}}."
        }
    },

    create(context) {
        const DEFAULT_VARIABLE_INDENT = 1;
        const DEFAULT_PARAMETER_INDENT = null; // For backwards compatibility, don't check parameter indentation unless specified in the config
        const DEFAULT_FUNCTION_BODY_INDENT = 1;

        let indentType = "space";
        let indentSize = 4;
        const options = {
            SwitchCase: 0,
            VariableDeclarator: {
                var: DEFAULT_VARIABLE_INDENT,
                let: DEFAULT_VARIABLE_INDENT,
                const: DEFAULT_VARIABLE_INDENT
            },
            outerIIFEBody: null,
            FunctionDeclaration: {
                parameters: DEFAULT_PARAMETER_INDENT,
                body: DEFAULT_FUNCTION_BODY_INDENT
            },
            FunctionExpression: {
                parameters: DEFAULT_PARAMETER_INDENT,
                body: DEFAULT_FUNCTION_BODY_INDENT
            },
            CallExpression: {
                arguments: DEFAULT_PARAMETER_INDENT
            },
            ArrayExpression: 1,
            ObjectExpression: 1
        };

        const sourceCode = context.sourceCode;

        if (context.options.length) {
            if (context.options[0] === "tab") {
                indentSize = 1;
                indentType = "tab";
            } else /* c8 ignore start */ if (typeof context.options[0] === "number") {
                indentSize = context.options[0];
                indentType = "space";
            }/* c8 ignore stop */

            if (context.options[1]) {
                const opts = context.options[1];

                options.SwitchCase = opts.SwitchCase || 0;
                const variableDeclaratorRules = opts.VariableDeclarator;

                if (typeof variableDeclaratorRules === "number") {
                    options.VariableDeclarator = {
                        var: variableDeclaratorRules,
                        let: variableDeclaratorRules,
                        const: variableDeclaratorRules
                    };
                } else if (typeof variableDeclaratorRules === "object") {
                    Object.assign(options.VariableDeclarator, variableDeclaratorRules);
                }

                if (typeof opts.outerIIFEBody === "number") {
                    options.outerIIFEBody = opts.outerIIFEBody;
                }

                if (typeof opts.MemberExpression === "number") {
                    options.MemberExpression = opts.MemberExpression;
                }

                if (typeof opts.FunctionDeclaration === "object") {
                    Object.assign(options.FunctionDeclaration, opts.FunctionDeclaration);
                }

                if (typeof opts.FunctionExpression === "object") {
                    Object.assign(options.FunctionExpression, opts.FunctionExpression);
                }

                if (typeof opts.CallExpression === "object") {
                    Object.assign(options.CallExpression, opts.CallExpression);
                }

                if (typeof opts.ArrayExpression === "number" || typeof opts.ArrayExpression === "string") {
                    options.ArrayExpression = opts.ArrayExpression;
                }

                if (typeof opts.ObjectExpression === "number" || typeof opts.ObjectExpression === "string") {
                    options.ObjectExpression = opts.ObjectExpression;
                }
            }
        }

        const caseIndentStore = {};

        /**
         * Creates an error message for a line, given the expected/actual indentation.
         * @param {int} expectedAmount The expected amount of indentation characters for this line
         * @param {int} actualSpaces The actual number of indentation spaces that were found on this line
         * @param {int} actualTabs The actual number of indentation tabs that were found on this line
         * @returns {string} An error message for this line
         */
        function createErrorMessageData(expectedAmount, actualSpaces, actualTabs) {
            const expectedStatement = `${expectedAmount} ${indentType}${expectedAmount === 1 ? "" : "s"}`; // e.g. "2 tabs"
            const foundSpacesWord = `space${actualSpaces === 1 ? "" : "s"}`; // e.g. "space"
            const foundTabsWord = `tab${actualTabs === 1 ? "" : "s"}`; // e.g. "tabs"
            let foundStatement;

            if (actualSpaces > 0 && actualTabs > 0) {
                foundStatement = `${actualSpaces} ${foundSpacesWord} and ${actualTabs} ${foundTabsWord}`; // e.g. "1 space and 2 tabs"
            } else if (actualSpaces > 0) {

                /*
                 * Abbreviate the message if the expected indentation is also spaces.
                 * e.g. 'Expected 4 spaces but found 2' rather than 'Expected 4 spaces but found 2 spaces'
                 */
                foundStatement = indentType === "space" ? actualSpaces : `${actualSpaces} ${foundSpacesWord}`;
            } else if (actualTabs > 0) {
                foundStatement = indentType === "tab" ? actualTabs : `${actualTabs} ${foundTabsWord}`;
            } else {
                foundStatement = "0";
            }
            return {
                expected: expectedStatement,
                actual: foundStatement
            };
        }

        /**
         * Reports a given indent violation
         * @param {ASTNode} node Node violating the indent rule
         * @param {int} needed Expected indentation character count
         * @param {int} gottenSpaces Indentation space count in the actual node/code
         * @param {int} gottenTabs Indentation tab count in the actual node/code
         * @param {Object} [loc] Error line and column location
         * @param {boolean} isLastNodeCheck Is the error for last node check
         * @returns {void}
         */
        function report(node, needed, gottenSpaces, gottenTabs, loc, isLastNodeCheck) {
            if (gottenSpaces && gottenTabs) {

                // To avoid conflicts with `no-mixed-spaces-and-tabs`, don't report lines that have both spaces and tabs.
                return;
            }

            const desiredIndent = (indentType === "space" ? " " : "\t").repeat(needed);

            const textRange = isLastNodeCheck
                ? [node.range[1] - node.loc.end.column, node.range[1] - node.loc.end.column + gottenSpaces + gottenTabs]
                : [node.range[0] - node.loc.start.column, node.range[0] - node.loc.start.column + gottenSpaces + gottenTabs];

            context.report({
                node,
                loc,
                messageId: "expected",
                data: createErrorMessageData(needed, gottenSpaces, gottenTabs),
                fix: fixer => fixer.replaceTextRange(textRange, desiredIndent)
            });
        }

        /**
         * Get the actual indent of node
         * @param {ASTNode|Token} node Node to examine
         * @param {boolean} [byLastLine=false] get indent of node's last line
         * @returns {Object} The node's indent. Contains keys `space` and `tab`, representing the indent of each character. Also
         * contains keys `goodChar` and `badChar`, where `goodChar` is the amount of the user's desired indentation character, and
         * `badChar` is the amount of the other indentation character.
         */
        function getNodeIndent(node, byLastLine) {
            const token = byLastLine ? sourceCode.getLastToken(node) : sourceCode.getFirstToken(node);
            const srcCharsBeforeNode = sourceCode.getText(token, token.loc.start.column).split("");
            const indentChars = srcCharsBeforeNode.slice(0, srcCharsBeforeNode.findIndex(char => char !== " " && char !== "\t"));
            const spaces = indentChars.filter(char => char === " ").length;
            const tabs = indentChars.filter(char => char === "\t").length;

            return {
                space: spaces,
                tab: tabs,
                goodChar: indentType === "space" ? spaces : tabs,
                badChar: indentType === "space" ? tabs : spaces
            };
        }

        /**
         * Checks node is the first in its own start line. By default it looks by start line.
         * @param {ASTNode} node The node to check
         * @param {boolean} [byEndLocation=false] Lookup based on start position or end
         * @returns {boolean} true if its the first in the its start line
         */
        function isNodeFirstInLine(node, byEndLocation) {
            const firstToken = byEndLocation === true ? sourceCode.getLastToken(node, 1) : sourceCode.getTokenBefore(node),
                startLine = byEndLocation === true ? node.loc.end.line : node.loc.start.line,
                endLine = firstToken ? firstToken.loc.end.line : -1;

            return startLine !== endLine;
        }

        /**
         * Check indent for node
         * @param {ASTNode} node Node to check
         * @param {int} neededIndent needed indent
         * @returns {void}
         */
        function checkNodeIndent(node, neededIndent) {
            const actualIndent = getNodeIndent(node, false);

            if (
                node.type !== "ArrayExpression" &&
                node.type !== "ObjectExpression" &&
                (actualIndent.goodChar !== neededIndent || actualIndent.badChar !== 0) &&
                isNodeFirstInLine(node)
            ) {
                report(node, neededIndent, actualIndent.space, actualIndent.tab);
            }

            if (node.type === "IfStatement" && node.alternate) {
                const elseToken = sourceCode.getTokenBefore(node.alternate);

                checkNodeIndent(elseToken, neededIndent);

                if (!isNodeFirstInLine(node.alternate)) {
                    checkNodeIndent(node.alternate, neededIndent);
                }
            }

            if (node.type === "TryStatement" && node.handler) {
                const catchToken = sourceCode.getFirstToken(node.handler);

                checkNodeIndent(catchToken, neededIndent);
            }

            if (node.type === "TryStatement" && node.finalizer) {
                const finallyToken = sourceCode.getTokenBefore(node.finalizer);

                checkNodeIndent(finallyToken, neededIndent);
            }

            if (node.type === "DoWhileStatement") {
                const whileToken = sourceCode.getTokenAfter(node.body);

                checkNodeIndent(whileToken, neededIndent);
            }
        }

        /**
         * Check indent for nodes list
         * @param {ASTNode[]} nodes list of node objects
         * @param {int} indent needed indent
         * @returns {void}
         */
        function checkNodesIndent(nodes, indent) {
            nodes.forEach(node => checkNodeIndent(node, indent));
        }

        /**
         * Check last node line indent this detects, that block closed correctly
         * @param {ASTNode} node Node to examine
         * @param {int} lastLineIndent needed indent
         * @returns {void}
         */
        function checkLastNodeLineIndent(node, lastLineIndent) {
            const lastToken = sourceCode.getLastToken(node);
            const endIndent = getNodeIndent(lastToken, true);

            if ((endIndent.goodChar !== lastLineIndent || endIndent.badChar !== 0) && isNodeFirstInLine(node, true)) {
                report(
                    node,
                    lastLineIndent,
                    endIndent.space,
                    endIndent.tab,
                    { line: lastToken.loc.start.line, column: lastToken.loc.start.column },
                    true
                );
            }
        }

        /**
         * Check last node line indent this detects, that block closed correctly
         * This function for more complicated return statement case, where closing parenthesis may be followed by ';'
         * @param {ASTNode} node Node to examine
         * @param {int} firstLineIndent first line needed indent
         * @returns {void}
         */
        function checkLastReturnStatementLineIndent(node, firstLineIndent) {

            /*
             * in case if return statement ends with ');' we have traverse back to ')'
             * otherwise we'll measure indent for ';' and replace ')'
             */
            const lastToken = sourceCode.getLastToken(node, astUtils.isClosingParenToken);
            const textBeforeClosingParenthesis = sourceCode.getText(lastToken, lastToken.loc.start.column).slice(0, -1);

            if (textBeforeClosingParenthesis.trim()) {

                // There are tokens before the closing paren, don't report this case
                return;
            }

            const endIndent = getNodeIndent(lastToken, true);

            if (endIndent.goodChar !== firstLineIndent) {
                report(
                    node,
                    firstLineIndent,
                    endIndent.space,
                    endIndent.tab,
                    { line: lastToken.loc.start.line, column: lastToken.loc.start.column },
                    true
                );
            }
        }

        /**
         * Check first node line indent is correct
         * @param {ASTNode} node Node to examine
         * @param {int} firstLineIndent needed indent
         * @returns {void}
         */
        function checkFirstNodeLineIndent(node, firstLineIndent) {
            const startIndent = getNodeIndent(node, false);

            if ((startIndent.goodChar !== firstLineIndent || startIndent.badChar !== 0) && isNodeFirstInLine(node)) {
                report(
                    node,
                    firstLineIndent,
                    startIndent.space,
                    startIndent.tab,
                    { line: node.loc.start.line, column: node.loc.start.column }
                );
            }
        }

        /**
         * Returns a parent node of given node based on a specified type
         * if not present then return null
         * @param {ASTNode} node node to examine
         * @param {string} type type that is being looked for
         * @param {string} stopAtList end points for the evaluating code
         * @returns {ASTNode|void} if found then node otherwise null
         */
        function getParentNodeByType(node, type, stopAtList) {
            let parent = node.parent;
            const stopAtSet = new Set(stopAtList || ["Program"]);

            while (parent.type !== type && !stopAtSet.has(parent.type) && parent.type !== "Program") {
                parent = parent.parent;
            }

            return parent.type === type ? parent : null;
        }

        /**
         * Returns the VariableDeclarator based on the current node
         * if not present then return null
         * @param {ASTNode} node node to examine
         * @returns {ASTNode|void} if found then node otherwise null
         */
        function getVariableDeclaratorNode(node) {
            return getParentNodeByType(node, "VariableDeclarator");
        }

        /**
         * Check to see if the node is part of the multi-line variable declaration.
         * Also if its on the same line as the varNode
         * @param {ASTNode} node node to check
         * @param {ASTNode} varNode variable declaration node to check against
         * @returns {boolean} True if all the above condition satisfy
         */
        function isNodeInVarOnTop(node, varNode) {
            return varNode &&
                varNode.parent.loc.start.line === node.loc.start.line &&
                varNode.parent.declarations.length > 1;
        }

        /**
         * Check to see if the argument before the callee node is multi-line and
         * there should only be 1 argument before the callee node
         * @param {ASTNode} node node to check
         * @returns {boolean} True if arguments are multi-line
         */
        function isArgBeforeCalleeNodeMultiline(node) {
            const parent = node.parent;

            if (parent.arguments.length >= 2 && parent.arguments[1] === node) {
                return parent.arguments[0].loc.end.line > parent.arguments[0].loc.start.line;
            }

            return false;
        }

        /**
         * Check to see if the node is a file level IIFE
         * @param {ASTNode} node The function node to check.
         * @returns {boolean} True if the node is the outer IIFE
         */
        function isOuterIIFE(node) {
            const parent = node.parent;
            let stmt = parent.parent;

            /*
             * Verify that the node is an IIEF
             */
            if (
                parent.type !== "CallExpression" ||
                parent.callee !== node) {

                return false;
            }

            /*
             * Navigate legal ancestors to determine whether this IIEF is outer
             */
            while (
                stmt.type === "UnaryExpression" && (
                    stmt.operator === "!" ||
                    stmt.operator === "~" ||
                    stmt.operator === "+" ||
                    stmt.operator === "-") ||
                stmt.type === "AssignmentExpression" ||
                stmt.type === "LogicalExpression" ||
                stmt.type === "SequenceExpression" ||
                stmt.type === "VariableDeclarator") {

                stmt = stmt.parent;
            }

            return ((
                stmt.type === "ExpressionStatement" ||
                stmt.type === "VariableDeclaration") &&
                stmt.parent && stmt.parent.type === "Program"
            );
        }

        /**
         * Check indent for function block content
         * @param {ASTNode} node A BlockStatement node that is inside of a function.
         * @returns {void}
         */
        function checkIndentInFunctionBlock(node) {

            /*
             * Search first caller in chain.
             * Ex.:
             *
             * Models <- Identifier
             *   .User
             *   .find()
             *   .exec(function() {
             *   // function body
             * });
             *
             * Looks for 'Models'
             */
            const calleeNode = node.parent; // FunctionExpression
            let indent;

            if (calleeNode.parent &&
                (calleeNode.parent.type === "Property" ||
                calleeNode.parent.type === "ArrayExpression")) {

                // If function is part of array or object, comma can be put at left
                indent = getNodeIndent(calleeNode, false).goodChar;
            } else {

                // If function is standalone, simple calculate indent
                indent = getNodeIndent(calleeNode).goodChar;
            }

            if (calleeNode.parent.type === "CallExpression") {
                const calleeParent = calleeNode.parent;

                if (calleeNode.type !== "FunctionExpression" && calleeNode.type !== "ArrowFunctionExpression") {
                    if (calleeParent && calleeParent.loc.start.line < node.loc.start.line) {
                        indent = getNodeIndent(calleeParent).goodChar;
                    }
                } else {
                    if (isArgBeforeCalleeNodeMultiline(calleeNode) &&
                        calleeParent.callee.loc.start.line === calleeParent.callee.loc.end.line &&
                        !isNodeFirstInLine(calleeNode)) {
                        indent = getNodeIndent(calleeParent).goodChar;
                    }
                }
            }

            /*
             * function body indent should be indent + indent size, unless this
             * is a FunctionDeclaration, FunctionExpression, or outer IIFE and the corresponding options are enabled.
             */
            let functionOffset = indentSize;

            if (options.outerIIFEBody !== null && isOuterIIFE(calleeNode)) {
                functionOffset = options.outerIIFEBody * indentSize;
            } else if (calleeNode.type === "FunctionExpression") {
                functionOffset = options.FunctionExpression.body * indentSize;
            } else if (calleeNode.type === "FunctionDeclaration") {
                functionOffset = options.FunctionDeclaration.body * indentSize;
            }
            indent += functionOffset;

            // check if the node is inside a variable
            const parentVarNode = getVariableDeclaratorNode(node);

            if (parentVarNode && isNodeInVarOnTop(node, parentVarNode)) {
                indent += indentSize * options.VariableDeclarator[parentVarNode.parent.kind];
            }

            if (node.body.length > 0) {
                checkNodesIndent(node.body, indent);
            }

            checkLastNodeLineIndent(node, indent - functionOffset);
        }

        /**
         * Checks if the given node starts and ends on the same line
         * @param {ASTNode} node The node to check
         * @returns {boolean} Whether or not the block starts and ends on the same line.
         */
        function isSingleLineNode(node) {
            const lastToken = sourceCode.getLastToken(node),
                startLine = node.loc.start.line,
                endLine = lastToken.loc.end.line;

            return startLine === endLine;
        }

        /**
         * Check indent for array block content or object block content
         * @param {ASTNode} node node to examine
         * @returns {void}
         */
        function checkIndentInArrayOrObjectBlock(node) {

            // Skip inline
            if (isSingleLineNode(node)) {
                return;
            }

            let elements = (node.type === "ArrayExpression") ? node.elements : node.properties;

            // filter out empty elements example would be [ , 2] so remove first element as espree considers it as null
            elements = elements.filter(elem => elem !== null);

            let nodeIndent;
            let elementsIndent;
            const parentVarNode = getVariableDeclaratorNode(node);

            // TODO - come up with a better strategy in future
            if (isNodeFirstInLine(node)) {
                const parent = node.parent;

                nodeIndent = getNodeIndent(parent).goodChar;
                if (!parentVarNode || parentVarNode.loc.start.line !== node.loc.start.line) {
                    if (parent.type !== "VariableDeclarator" || parentVarNode === parentVarNode.parent.declarations[0]) {
                        if (parent.type === "VariableDeclarator" && parentVarNode.loc.start.line === parent.loc.start.line) {
                            nodeIndent += (indentSize * options.VariableDeclarator[parentVarNode.parent.kind]);
                        } else if (parent.type === "ObjectExpression" || parent.type === "ArrayExpression") {
                            const parentElements = node.parent.type === "ObjectExpression" ? node.parent.properties : node.parent.elements;

                            if (parentElements[0] &&
                                    parentElements[0].loc.start.line === parent.loc.start.line &&
                                    parentElements[0].loc.end.line !== parent.loc.start.line) {

                                /*
                                 * If the first element of the array spans multiple lines, don't increase the expected indentation of the rest.
                                 * e.g. [{
                                 *        foo: 1
                                 *      },
                                 *      {
                                 *        bar: 1
                                 *      }]
                                 * the second object is not indented.
                                 */
                            } else if (typeof options[parent.type] === "number") {
                                nodeIndent += options[parent.type] * indentSize;
                            } else {
                                nodeIndent = parentElements[0].loc.start.column;
                            }
                        } else if (parent.type === "CallExpression" || parent.type === "NewExpression") {
                            if (typeof options.CallExpression.arguments === "number") {
                                nodeIndent += options.CallExpression.arguments * indentSize;
                            } else if (options.CallExpression.arguments === "first") {
                                if (parent.arguments.includes(node)) {
                                    nodeIndent = parent.arguments[0].loc.start.column;
                                }
                            } else {
                                nodeIndent += indentSize;
                            }
                        } else if (parent.type === "LogicalExpression" || parent.type === "ArrowFunctionExpression") {
                            nodeIndent += indentSize;
                        }
                    }
                }

                checkFirstNodeLineIndent(node, nodeIndent);
            } else {
                nodeIndent = getNodeIndent(node).goodChar;
            }

            if (options[node.type] === "first") {
                elementsIndent = elements.length ? elements[0].loc.start.column : 0; // If there are no elements, elementsIndent doesn't matter.
            } else {
                elementsIndent = nodeIndent + indentSize * options[node.type];
            }

            /*
             * Check if the node is a multiple variable declaration; if so, then
             * make sure indentation takes that into account.
             */
            if (isNodeInVarOnTop(node, parentVarNode)) {
                elementsIndent += indentSize * options.VariableDeclarator[parentVarNode.parent.kind];
            }

            checkNodesIndent(elements, elementsIndent);

            if (elements.length > 0) {

                // Skip last block line check if last item in same line
                if (elements[elements.length - 1].loc.end.line === node.loc.end.line) {
                    return;
                }
            }

            checkLastNodeLineIndent(node, nodeIndent +
                (isNodeInVarOnTop(node, parentVarNode) ? options.VariableDeclarator[parentVarNode.parent.kind] * indentSize : 0));
        }

        /**
         * Check if the node or node body is a BlockStatement or not
         * @param {ASTNode} node node to test
         * @returns {boolean} True if it or its body is a block statement
         */
        function isNodeBodyBlock(node) {
            return node.type === "BlockStatement" || node.type === "ClassBody" || (node.body && node.body.type === "BlockStatement") ||
                (node.consequent && node.consequent.type === "BlockStatement");
        }

        /**
         * Check indentation for blocks
         * @param {ASTNode} node node to check
         * @returns {void}
         */
        function blockIndentationCheck(node) {

            // Skip inline blocks
            if (isSingleLineNode(node)) {
                return;
            }

            if (node.parent && (
                node.parent.type === "FunctionExpression" ||
                node.parent.type === "FunctionDeclaration" ||
                node.parent.type === "ArrowFunctionExpression")
            ) {
                checkIndentInFunctionBlock(node);
                return;
            }

            let indent;
            let nodesToCheck = [];

            /*
             * For this statements we should check indent from statement beginning,
             * not from the beginning of the block.
             */
            const statementsWithProperties = [
                "IfStatement", "WhileStatement", "ForStatement", "ForInStatement", "ForOfStatement", "DoWhileStatement", "ClassDeclaration", "TryStatement"
            ];

            if (node.parent && statementsWithProperties.includes(node.parent.type) && isNodeBodyBlock(node)) {
                indent = getNodeIndent(node.parent).goodChar;
            } else if (node.parent && node.parent.type === "CatchClause") {
                indent = getNodeIndent(node.parent.parent).goodChar;
            } else {
                indent = getNodeIndent(node).goodChar;
            }

            if (node.type === "IfStatement" && node.consequent.type !== "BlockStatement") {
                nodesToCheck = [node.consequent];
            } else if (Array.isArray(node.body)) {
                nodesToCheck = node.body;
            } else {
                nodesToCheck = [node.body];
            }

            if (nodesToCheck.length > 0) {
                checkNodesIndent(nodesToCheck, indent + indentSize);
            }

            if (node.type === "BlockStatement") {
                checkLastNodeLineIndent(node, indent);
            }
        }

        /**
         * Filter out the elements which are on the same line of each other or the node.
         * basically have only 1 elements from each line except the variable declaration line.
         * @param {ASTNode} node Variable declaration node
         * @returns {ASTNode[]} Filtered elements
         */
        function filterOutSameLineVars(node) {
            return node.declarations.reduce((finalCollection, elem) => {
                const lastElem = finalCollection[finalCollection.length - 1];

                if ((elem.loc.start.line !== node.loc.start.line && !lastElem) ||
                    (lastElem && lastElem.loc.start.line !== elem.loc.start.line)) {
                    finalCollection.push(elem);
                }

                return finalCollection;
            }, []);
        }

        /**
         * Check indentation for variable declarations
         * @param {ASTNode} node node to examine
         * @returns {void}
         */
        function checkIndentInVariableDeclarations(node) {
            const elements = filterOutSameLineVars(node);
            const nodeIndent = getNodeIndent(node).goodChar;
            const lastElement = elements[elements.length - 1];

            const elementsIndent = nodeIndent + indentSize * options.VariableDeclarator[node.kind];

            checkNodesIndent(elements, elementsIndent);

            // Only check the last line if there is any token after the last item
            if (sourceCode.getLastToken(node).loc.end.line <= lastElement.loc.end.line) {
                return;
            }

            const tokenBeforeLastElement = sourceCode.getTokenBefore(lastElement);

            if (tokenBeforeLastElement.value === ",") {

                // Special case for comma-first syntax where the semicolon is indented
                checkLastNodeLineIndent(node, getNodeIndent(tokenBeforeLastElement).goodChar);
            } else {
                checkLastNodeLineIndent(node, elementsIndent - indentSize);
            }
        }

        /**
         * Check and decide whether to check for indentation for blockless nodes
         * Scenarios are for or while statements without braces around them
         * @param {ASTNode} node node to examine
         * @returns {void}
         */
        function blockLessNodes(node) {
            if (node.body.type !== "BlockStatement") {
                blockIndentationCheck(node);
            }
        }

        /**
         * Returns the expected indentation for the case statement
         * @param {ASTNode} node node to examine
         * @param {int} [providedSwitchIndent] indent for switch statement
         * @returns {int} indent size
         */
        function expectedCaseIndent(node, providedSwitchIndent) {
            const switchNode = (node.type === "SwitchStatement") ? node : node.parent;
            const switchIndent = typeof providedSwitchIndent === "undefined"
                ? getNodeIndent(switchNode).goodChar
                : providedSwitchIndent;
            let caseIndent;

            if (caseIndentStore[switchNode.loc.start.line]) {
                return caseIndentStore[switchNode.loc.start.line];
            }

            if (switchNode.cases.length > 0 && options.SwitchCase === 0) {
                caseIndent = switchIndent;
            } else {
                caseIndent = switchIndent + (indentSize * options.SwitchCase);
            }

            caseIndentStore[switchNode.loc.start.line] = caseIndent;
            return caseIndent;

        }

        /**
         * Checks whether a return statement is wrapped in ()
         * @param {ASTNode} node node to examine
         * @returns {boolean} the result
         */
        function isWrappedInParenthesis(node) {
            const regex = /^return\s*?\(\s*?\);*?/u;

            const statementWithoutArgument = sourceCode.getText(node).replace(
                sourceCode.getText(node.argument), ""
            );

            return regex.test(statementWithoutArgument);
        }

        return {
            Program(node) {
                if (node.body.length > 0) {

                    // Root nodes should have no indent
                    checkNodesIndent(node.body, getNodeIndent(node).goodChar);
                }
            },

            ClassBody: blockIndentationCheck,

            BlockStatement: blockIndentationCheck,

            WhileStatement: blockLessNodes,

            ForStatement: blockLessNodes,

            ForInStatement: blockLessNodes,

            ForOfStatement: blockLessNodes,

            DoWhileStatement: blockLessNodes,

            IfStatement(node) {
                if (node.consequent.type !== "BlockStatement" && node.consequent.loc.start.line > node.loc.start.line) {
                    blockIndentationCheck(node);
                }
            },

            VariableDeclaration(node) {
                if (node.declarations[node.declarations.length - 1].loc.start.line > node.declarations[0].loc.start.line) {
                    checkIndentInVariableDeclarations(node);
                }
            },

            ObjectExpression(node) {
                checkIndentInArrayOrObjectBlock(node);
            },

            ArrayExpression(node) {
                checkIndentInArrayOrObjectBlock(node);
            },

            MemberExpression(node) {

                if (typeof options.MemberExpression === "undefined") {
                    return;
                }

                if (isSingleLineNode(node)) {
                    return;
                }

                /*
                 * The typical layout of variable declarations and assignments
                 * alter the expectation of correct indentation. Skip them.
                 * TODO: Add appropriate configuration options for variable
                 * declarations and assignments.
                 */
                if (getParentNodeByType(node, "VariableDeclarator", ["FunctionExpression", "ArrowFunctionExpression"])) {
                    return;
                }

                if (getParentNodeByType(node, "AssignmentExpression", ["FunctionExpression"])) {
                    return;
                }

                const propertyIndent = getNodeIndent(node).goodChar + indentSize * options.MemberExpression;

                const checkNodes = [node.property];

                const dot = sourceCode.getTokenBefore(node.property);

                if (dot.type === "Punctuator" && dot.value === ".") {
                    checkNodes.push(dot);
                }

                checkNodesIndent(checkNodes, propertyIndent);
            },

            SwitchStatement(node) {

                // Switch is not a 'BlockStatement'
                const switchIndent = getNodeIndent(node).goodChar;
                const caseIndent = expectedCaseIndent(node, switchIndent);

                checkNodesIndent(node.cases, caseIndent);

                checkLastNodeLineIndent(node, switchIndent);
            },

            SwitchCase(node) {

                // Skip inline cases
                if (isSingleLineNode(node)) {
                    return;
                }
                const caseIndent = expectedCaseIndent(node);

                checkNodesIndent(node.consequent, caseIndent + indentSize);
            },

            FunctionDeclaration(node) {
                if (isSingleLineNode(node)) {
                    return;
                }
                if (options.FunctionDeclaration.parameters === "first" && node.params.length) {
                    checkNodesIndent(node.params.slice(1), node.params[0].loc.start.column);
                } else if (options.FunctionDeclaration.parameters !== null) {
                    checkNodesIndent(node.params, getNodeIndent(node).goodChar + indentSize * options.FunctionDeclaration.parameters);
                }
            },

            FunctionExpression(node) {
                if (isSingleLineNode(node)) {
                    return;
                }
                if (options.FunctionExpression.parameters === "first" && node.params.length) {
                    checkNodesIndent(node.params.slice(1), node.params[0].loc.start.column);
                } else if (options.FunctionExpression.parameters !== null) {
                    checkNodesIndent(node.params, getNodeIndent(node).goodChar + indentSize * options.FunctionExpression.parameters);
                }
            },

            ReturnStatement(node) {
                if (isSingleLineNode(node)) {
                    return;
                }

                const firstLineIndent = getNodeIndent(node).goodChar;

                // in case if return statement is wrapped in parenthesis
                if (isWrappedInParenthesis(node)) {
                    checkLastReturnStatementLineIndent(node, firstLineIndent);
                } else {
                    checkNodeIndent(node, firstLineIndent);
                }
            },

            CallExpression(node) {
                if (isSingleLineNode(node)) {
                    return;
                }
                if (options.CallExpression.arguments === "first" && node.arguments.length) {
                    checkNodesIndent(node.arguments.slice(1), node.arguments[0].loc.start.column);
                } else if (options.CallExpression.arguments !== null) {
                    checkNodesIndent(node.arguments, getNodeIndent(node).goodChar + indentSize * options.CallExpression.arguments);
                }
            }

        };

    }
};