/**
							 | 
						|
								 * @fileoverview Rule to flag use of variables before they are defined
							 | 
						|
								 * @author Ilya Volodin
							 | 
						|
								 */
							 | 
						|
								
							 | 
						|
								"use strict";
							 | 
						|
								
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								// Helpers
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								
							 | 
						|
								const SENTINEL_TYPE = /^(?:(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|CatchClause|ImportDeclaration|ExportNamedDeclaration)$/u;
							 | 
						|
								const FOR_IN_OF_TYPE = /^For(?:In|Of)Statement$/u;
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Parses a given value as options.
							 | 
						|
								 * @param {any} options A value to parse.
							 | 
						|
								 * @returns {Object} The parsed options.
							 | 
						|
								 */
							 | 
						|
								function parseOptions(options) {
							 | 
						|
								    let functions = true;
							 | 
						|
								    let classes = true;
							 | 
						|
								    let variables = true;
							 | 
						|
								    let allowNamedExports = false;
							 | 
						|
								
							 | 
						|
								    if (typeof options === "string") {
							 | 
						|
								        functions = (options !== "nofunc");
							 | 
						|
								    } else if (typeof options === "object" && options !== null) {
							 | 
						|
								        functions = options.functions !== false;
							 | 
						|
								        classes = options.classes !== false;
							 | 
						|
								        variables = options.variables !== false;
							 | 
						|
								        allowNamedExports = !!options.allowNamedExports;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return { functions, classes, variables, allowNamedExports };
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Checks whether or not a given location is inside of the range of a given node.
							 | 
						|
								 * @param {ASTNode} node An node to check.
							 | 
						|
								 * @param {number} location A location to check.
							 | 
						|
								 * @returns {boolean} `true` if the location is inside of the range of the node.
							 | 
						|
								 */
							 | 
						|
								function isInRange(node, location) {
							 | 
						|
								    return node && node.range[0] <= location && location <= node.range[1];
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Checks whether or not a given location is inside of the range of a class static initializer.
							 | 
						|
								 * Static initializers are static blocks and initializers of static fields.
							 | 
						|
								 * @param {ASTNode} node `ClassBody` node to check static initializers.
							 | 
						|
								 * @param {number} location A location to check.
							 | 
						|
								 * @returns {boolean} `true` if the location is inside of a class static initializer.
							 | 
						|
								 */
							 | 
						|
								function isInClassStaticInitializerRange(node, location) {
							 | 
						|
								    return node.body.some(classMember => (
							 | 
						|
								        (
							 | 
						|
								            classMember.type === "StaticBlock" &&
							 | 
						|
								            isInRange(classMember, location)
							 | 
						|
								        ) ||
							 | 
						|
								        (
							 | 
						|
								            classMember.type === "PropertyDefinition" &&
							 | 
						|
								            classMember.static &&
							 | 
						|
								            classMember.value &&
							 | 
						|
								            isInRange(classMember.value, location)
							 | 
						|
								        )
							 | 
						|
								    ));
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Checks whether a given scope is the scope of a class static initializer.
							 | 
						|
								 * Static initializers are static blocks and initializers of static fields.
							 | 
						|
								 * @param {eslint-scope.Scope} scope A scope to check.
							 | 
						|
								 * @returns {boolean} `true` if the scope is a class static initializer scope.
							 | 
						|
								 */
							 | 
						|
								function isClassStaticInitializerScope(scope) {
							 | 
						|
								    if (scope.type === "class-static-block") {
							 | 
						|
								        return true;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    if (scope.type === "class-field-initializer") {
							 | 
						|
								
							 | 
						|
								        // `scope.block` is PropertyDefinition#value node
							 | 
						|
								        const propertyDefinition = scope.block.parent;
							 | 
						|
								
							 | 
						|
								        return propertyDefinition.static;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return false;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Checks whether a given reference is evaluated in an execution context
							 | 
						|
								 * that isn't the one where the variable it refers to is defined.
							 | 
						|
								 * Execution contexts are:
							 | 
						|
								 * - top-level
							 | 
						|
								 * - functions
							 | 
						|
								 * - class field initializers (implicit functions)
							 | 
						|
								 * - class static blocks (implicit functions)
							 | 
						|
								 * Static class field initializers and class static blocks are automatically run during the class definition evaluation,
							 | 
						|
								 * and therefore we'll consider them as a part of the parent execution context.
							 | 
						|
								 * Example:
							 | 
						|
								 *
							 | 
						|
								 *   const x = 1;
							 | 
						|
								 *
							 | 
						|
								 *   x; // returns `false`
							 | 
						|
								 *   () => x; // returns `true`
							 | 
						|
								 *
							 | 
						|
								 *   class C {
							 | 
						|
								 *       field = x; // returns `true`
							 | 
						|
								 *       static field = x; // returns `false`
							 | 
						|
								 *
							 | 
						|
								 *       method() {
							 | 
						|
								 *           x; // returns `true`
							 | 
						|
								 *       }
							 | 
						|
								 *
							 | 
						|
								 *       static method() {
							 | 
						|
								 *           x; // returns `true`
							 | 
						|
								 *       }
							 | 
						|
								 *
							 | 
						|
								 *       static {
							 | 
						|
								 *           x; // returns `false`
							 | 
						|
								 *       }
							 | 
						|
								 *   }
							 | 
						|
								 * @param {eslint-scope.Reference} reference A reference to check.
							 | 
						|
								 * @returns {boolean} `true` if the reference is from a separate execution context.
							 | 
						|
								 */
							 | 
						|
								function isFromSeparateExecutionContext(reference) {
							 | 
						|
								    const variable = reference.resolved;
							 | 
						|
								    let scope = reference.from;
							 | 
						|
								
							 | 
						|
								    // Scope#variableScope represents execution context
							 | 
						|
								    while (variable.scope.variableScope !== scope.variableScope) {
							 | 
						|
								        if (isClassStaticInitializerScope(scope.variableScope)) {
							 | 
						|
								            scope = scope.variableScope.upper;
							 | 
						|
								        } else {
							 | 
						|
								            return true;
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return false;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Checks whether or not a given reference is evaluated during the initialization of its variable.
							 | 
						|
								 *
							 | 
						|
								 * This returns `true` in the following cases:
							 | 
						|
								 *
							 | 
						|
								 *     var a = a
							 | 
						|
								 *     var [a = a] = list
							 | 
						|
								 *     var {a = a} = obj
							 | 
						|
								 *     for (var a in a) {}
							 | 
						|
								 *     for (var a of a) {}
							 | 
						|
								 *     var C = class { [C]; };
							 | 
						|
								 *     var C = class { static foo = C; };
							 | 
						|
								 *     var C = class { static { foo = C; } };
							 | 
						|
								 *     class C extends C {}
							 | 
						|
								 *     class C extends (class { static foo = C; }) {}
							 | 
						|
								 *     class C { [C]; }
							 | 
						|
								 * @param {Reference} reference A reference to check.
							 | 
						|
								 * @returns {boolean} `true` if the reference is evaluated during the initialization.
							 | 
						|
								 */
							 | 
						|
								function isEvaluatedDuringInitialization(reference) {
							 | 
						|
								    if (isFromSeparateExecutionContext(reference)) {
							 | 
						|
								
							 | 
						|
								        /*
							 | 
						|
								         * Even if the reference appears in the initializer, it isn't evaluated during the initialization.
							 | 
						|
								         * For example, `const x = () => x;` is valid.
							 | 
						|
								         */
							 | 
						|
								        return false;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    const location = reference.identifier.range[1];
							 | 
						|
								    const definition = reference.resolved.defs[0];
							 | 
						|
								
							 | 
						|
								    if (definition.type === "ClassName") {
							 | 
						|
								
							 | 
						|
								        // `ClassDeclaration` or `ClassExpression`
							 | 
						|
								        const classDefinition = definition.node;
							 | 
						|
								
							 | 
						|
								        return (
							 | 
						|
								            isInRange(classDefinition, location) &&
							 | 
						|
								
							 | 
						|
								            /*
							 | 
						|
								             * Class binding is initialized before running static initializers.
							 | 
						|
								             * For example, `class C { static foo = C; static { bar = C; } }` is valid.
							 | 
						|
								             */
							 | 
						|
								            !isInClassStaticInitializerRange(classDefinition.body, location)
							 | 
						|
								        );
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    let node = definition.name.parent;
							 | 
						|
								
							 | 
						|
								    while (node) {
							 | 
						|
								        if (node.type === "VariableDeclarator") {
							 | 
						|
								            if (isInRange(node.init, location)) {
							 | 
						|
								                return true;
							 | 
						|
								            }
							 | 
						|
								            if (FOR_IN_OF_TYPE.test(node.parent.parent.type) &&
							 | 
						|
								                isInRange(node.parent.parent.right, location)
							 | 
						|
								            ) {
							 | 
						|
								                return true;
							 | 
						|
								            }
							 | 
						|
								            break;
							 | 
						|
								        } else if (node.type === "AssignmentPattern") {
							 | 
						|
								            if (isInRange(node.right, location)) {
							 | 
						|
								                return true;
							 | 
						|
								            }
							 | 
						|
								        } else if (SENTINEL_TYPE.test(node.type)) {
							 | 
						|
								            break;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        node = node.parent;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return false;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								// Rule Definition
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								
							 | 
						|
								/** @type {import('../shared/types').Rule} */
							 | 
						|
								module.exports = {
							 | 
						|
								    meta: {
							 | 
						|
								        type: "problem",
							 | 
						|
								
							 | 
						|
								        docs: {
							 | 
						|
								            description: "Disallow the use of variables before they are defined",
							 | 
						|
								            recommended: false,
							 | 
						|
								            url: "https://eslint.org/docs/latest/rules/no-use-before-define"
							 | 
						|
								        },
							 | 
						|
								
							 | 
						|
								        schema: [
							 | 
						|
								            {
							 | 
						|
								                oneOf: [
							 | 
						|
								                    {
							 | 
						|
								                        enum: ["nofunc"]
							 | 
						|
								                    },
							 | 
						|
								                    {
							 | 
						|
								                        type: "object",
							 | 
						|
								                        properties: {
							 | 
						|
								                            functions: { type: "boolean" },
							 | 
						|
								                            classes: { type: "boolean" },
							 | 
						|
								                            variables: { type: "boolean" },
							 | 
						|
								                            allowNamedExports: { type: "boolean" }
							 | 
						|
								                        },
							 | 
						|
								                        additionalProperties: false
							 | 
						|
								                    }
							 | 
						|
								                ]
							 | 
						|
								            }
							 | 
						|
								        ],
							 | 
						|
								
							 | 
						|
								        messages: {
							 | 
						|
								            usedBeforeDefined: "'{{name}}' was used before it was defined."
							 | 
						|
								        }
							 | 
						|
								    },
							 | 
						|
								
							 | 
						|
								    create(context) {
							 | 
						|
								        const options = parseOptions(context.options[0]);
							 | 
						|
								        const sourceCode = context.sourceCode;
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Determines whether a given reference should be checked.
							 | 
						|
								         *
							 | 
						|
								         * Returns `false` if the reference is:
							 | 
						|
								         * - initialization's (e.g., `let a = 1`).
							 | 
						|
								         * - referring to an undefined variable (i.e., if it's an unresolved reference).
							 | 
						|
								         * - referring to a variable that is defined, but not in the given source code
							 | 
						|
								         *   (e.g., global environment variable or `arguments` in functions).
							 | 
						|
								         * - allowed by options.
							 | 
						|
								         * @param {eslint-scope.Reference} reference The reference
							 | 
						|
								         * @returns {boolean} `true` if the reference should be checked
							 | 
						|
								         */
							 | 
						|
								        function shouldCheck(reference) {
							 | 
						|
								            if (reference.init) {
							 | 
						|
								                return false;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            const { identifier } = reference;
							 | 
						|
								
							 | 
						|
								            if (
							 | 
						|
								                options.allowNamedExports &&
							 | 
						|
								                identifier.parent.type === "ExportSpecifier" &&
							 | 
						|
								                identifier.parent.local === identifier
							 | 
						|
								            ) {
							 | 
						|
								                return false;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            const variable = reference.resolved;
							 | 
						|
								
							 | 
						|
								            if (!variable || variable.defs.length === 0) {
							 | 
						|
								                return false;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            const definitionType = variable.defs[0].type;
							 | 
						|
								
							 | 
						|
								            if (!options.functions && definitionType === "FunctionName") {
							 | 
						|
								                return false;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            if (
							 | 
						|
								                (
							 | 
						|
								                    !options.variables && definitionType === "Variable" ||
							 | 
						|
								                    !options.classes && definitionType === "ClassName"
							 | 
						|
								                ) &&
							 | 
						|
								
							 | 
						|
								                // don't skip checking the reference if it's in the same execution context, because of TDZ
							 | 
						|
								                isFromSeparateExecutionContext(reference)
							 | 
						|
								            ) {
							 | 
						|
								                return false;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            return true;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Finds and validates all references in a given scope and its child scopes.
							 | 
						|
								         * @param {eslint-scope.Scope} scope The scope object.
							 | 
						|
								         * @returns {void}
							 | 
						|
								         */
							 | 
						|
								        function checkReferencesInScope(scope) {
							 | 
						|
								            scope.references.filter(shouldCheck).forEach(reference => {
							 | 
						|
								                const variable = reference.resolved;
							 | 
						|
								                const definitionIdentifier = variable.defs[0].name;
							 | 
						|
								
							 | 
						|
								                if (
							 | 
						|
								                    reference.identifier.range[1] < definitionIdentifier.range[1] ||
							 | 
						|
								                    isEvaluatedDuringInitialization(reference)
							 | 
						|
								                ) {
							 | 
						|
								                    context.report({
							 | 
						|
								                        node: reference.identifier,
							 | 
						|
								                        messageId: "usedBeforeDefined",
							 | 
						|
								                        data: reference.identifier
							 | 
						|
								                    });
							 | 
						|
								                }
							 | 
						|
								            });
							 | 
						|
								
							 | 
						|
								            scope.childScopes.forEach(checkReferencesInScope);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        return {
							 | 
						|
								            Program(node) {
							 | 
						|
								                checkReferencesInScope(sourceCode.getScope(node));
							 | 
						|
								            }
							 | 
						|
								        };
							 | 
						|
								    }
							 | 
						|
								};
							 |