| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 | /** * @fileoverview Rule to enforce var declarations are only at the top of a function. * @author Danny Fritz * @author Gyandeep Singh */"use strict";//------------------------------------------------------------------------------// Rule Definition//------------------------------------------------------------------------------module.exports = {    meta: {        docs: {            description: "require `var` declarations be placed at the top of their containing scope",            category: "Best Practices",            recommended: false,            url: "https://eslint.org/docs/rules/vars-on-top"        },        schema: []    },    create(context) {        const errorMessage = "All 'var' declarations must be at the top of the function scope.";        //--------------------------------------------------------------------------        // Helpers        //--------------------------------------------------------------------------        /**         * @param {ASTNode} node - any node         * @returns {boolean} whether the given node structurally represents a directive         */        function looksLikeDirective(node) {            return node.type === "ExpressionStatement" &&                node.expression.type === "Literal" && typeof node.expression.value === "string";        }        /**         * Check to see if its a ES6 import declaration         * @param {ASTNode} node - any node         * @returns {boolean} whether the given node represents a import declaration         */        function looksLikeImport(node) {            return node.type === "ImportDeclaration" || node.type === "ImportSpecifier" ||                node.type === "ImportDefaultSpecifier" || node.type === "ImportNamespaceSpecifier";        }        /**         * Checks whether a given node is a variable declaration or not.         *         * @param {ASTNode} node - any node         * @returns {boolean} `true` if the node is a variable declaration.         */        function isVariableDeclaration(node) {            return (                node.type === "VariableDeclaration" ||                (                    node.type === "ExportNamedDeclaration" &&                    node.declaration &&                    node.declaration.type === "VariableDeclaration"                )            );        }        /**         * Checks whether this variable is on top of the block body         * @param {ASTNode} node - The node to check         * @param {ASTNode[]} statements - collection of ASTNodes for the parent node block         * @returns {boolean} True if var is on top otherwise false         */        function isVarOnTop(node, statements) {            const l = statements.length;            let i = 0;            // skip over directives            for (; i < l; ++i) {                if (!looksLikeDirective(statements[i]) && !looksLikeImport(statements[i])) {                    break;                }            }            for (; i < l; ++i) {                if (!isVariableDeclaration(statements[i])) {                    return false;                }                if (statements[i] === node) {                    return true;                }            }            return false;        }        /**         * Checks whether variable is on top at the global level         * @param {ASTNode} node - The node to check         * @param {ASTNode} parent - Parent of the node         * @returns {void}         */        function globalVarCheck(node, parent) {            if (!isVarOnTop(node, parent.body)) {                context.report({ node, message: errorMessage });            }        }        /**         * Checks whether variable is on top at functional block scope level         * @param {ASTNode} node - The node to check         * @param {ASTNode} parent - Parent of the node         * @param {ASTNode} grandParent - Parent of the node's parent         * @returns {void}         */        function blockScopeVarCheck(node, parent, grandParent) {            if (!(/Function/.test(grandParent.type) &&                    parent.type === "BlockStatement" &&                    isVarOnTop(node, parent.body))) {                context.report({ node, message: errorMessage });            }        }        //--------------------------------------------------------------------------        // Public API        //--------------------------------------------------------------------------        return {            "VariableDeclaration[kind='var']"(node) {                if (node.parent.type === "ExportNamedDeclaration") {                    globalVarCheck(node.parent, node.parent.parent);                } else if (node.parent.type === "Program") {                    globalVarCheck(node, node.parent);                } else {                    blockScopeVarCheck(node, node.parent, node.parent.parent);                }            }        };    }};
 |