| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 | /** * @fileoverview Rule to enforce grouped require statements for Node.JS * @author Raphael Pigulla */"use strict";//------------------------------------------------------------------------------// Rule Definition//------------------------------------------------------------------------------module.exports = {    meta: {        docs: {            description: "disallow `require` calls to be mixed with regular variable declarations",            category: "Node.js and CommonJS",            recommended: false,            url: "https://eslint.org/docs/rules/no-mixed-requires"        },        schema: [            {                oneOf: [                    {                        type: "boolean"                    },                    {                        type: "object",                        properties: {                            grouping: {                                type: "boolean"                            },                            allowCall: {                                type: "boolean"                            }                        },                        additionalProperties: false                    }                ]            }        ]    },    create(context) {        const options = context.options[0];        let grouping = false,            allowCall = false;        if (typeof options === "object") {            grouping = options.grouping;            allowCall = options.allowCall;        } else {            grouping = !!options;        }        /**         * Returns the list of built-in modules.         *         * @returns {string[]} An array of built-in Node.js modules.         */        function getBuiltinModules() {            /*             * This list is generated using:             * `require("repl")._builtinLibs.concat('repl').sort()`             * This particular list is as per nodejs v0.12.2 and iojs v0.7.1             */            return [                "assert", "buffer", "child_process", "cluster", "crypto",                "dgram", "dns", "domain", "events", "fs", "http", "https",                "net", "os", "path", "punycode", "querystring", "readline",                "repl", "smalloc", "stream", "string_decoder", "tls", "tty",                "url", "util", "v8", "vm", "zlib"            ];        }        const BUILTIN_MODULES = getBuiltinModules();        const DECL_REQUIRE = "require",            DECL_UNINITIALIZED = "uninitialized",            DECL_OTHER = "other";        const REQ_CORE = "core",            REQ_FILE = "file",            REQ_MODULE = "module",            REQ_COMPUTED = "computed";        /**         * Determines the type of a declaration statement.         * @param {ASTNode} initExpression The init node of the VariableDeclarator.         * @returns {string} The type of declaration represented by the expression.         */        function getDeclarationType(initExpression) {            if (!initExpression) {                // "var x;"                return DECL_UNINITIALIZED;            }            if (initExpression.type === "CallExpression" &&                initExpression.callee.type === "Identifier" &&                initExpression.callee.name === "require"            ) {                // "var x = require('util');"                return DECL_REQUIRE;            }            if (allowCall &&                initExpression.type === "CallExpression" &&                initExpression.callee.type === "CallExpression"            ) {                // "var x = require('diagnose')('sub-module');"                return getDeclarationType(initExpression.callee);            }            if (initExpression.type === "MemberExpression") {                // "var x = require('glob').Glob;"                return getDeclarationType(initExpression.object);            }            // "var x = 42;"            return DECL_OTHER;        }        /**         * Determines the type of module that is loaded via require.         * @param {ASTNode} initExpression The init node of the VariableDeclarator.         * @returns {string} The module type.         */        function inferModuleType(initExpression) {            if (initExpression.type === "MemberExpression") {                // "var x = require('glob').Glob;"                return inferModuleType(initExpression.object);            }            if (initExpression.arguments.length === 0) {                // "var x = require();"                return REQ_COMPUTED;            }            const arg = initExpression.arguments[0];            if (arg.type !== "Literal" || typeof arg.value !== "string") {                // "var x = require(42);"                return REQ_COMPUTED;            }            if (BUILTIN_MODULES.indexOf(arg.value) !== -1) {                // "var fs = require('fs');"                return REQ_CORE;            }            if (/^\.{0,2}\//.test(arg.value)) {                // "var utils = require('./utils');"                return REQ_FILE;            }            // "var async = require('async');"            return REQ_MODULE;        }        /**         * Check if the list of variable declarations is mixed, i.e. whether it         * contains both require and other declarations.         * @param {ASTNode} declarations The list of VariableDeclarators.         * @returns {boolean} True if the declarations are mixed, false if not.         */        function isMixed(declarations) {            const contains = {};            declarations.forEach(declaration => {                const type = getDeclarationType(declaration.init);                contains[type] = true;            });            return !!(                contains[DECL_REQUIRE] &&                (contains[DECL_UNINITIALIZED] || contains[DECL_OTHER])            );        }        /**         * Check if all require declarations in the given list are of the same         * type.         * @param {ASTNode} declarations The list of VariableDeclarators.         * @returns {boolean} True if the declarations are grouped, false if not.         */        function isGrouped(declarations) {            const found = {};            declarations.forEach(declaration => {                if (getDeclarationType(declaration.init) === DECL_REQUIRE) {                    found[inferModuleType(declaration.init)] = true;                }            });            return Object.keys(found).length <= 1;        }        return {            VariableDeclaration(node) {                if (isMixed(node.declarations)) {                    context.report({ node, message: "Do not mix 'require' and other declarations." });                } else if (grouping && !isGrouped(node.declarations)) {                    context.report({ node, message: "Do not mix core, module, file and computed requires." });                }            }        };    }};
 |