2 * @fileoverview Rule to enforce grouped require statements for Node.JS
3 * @author Raphael Pigulla
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
17 description: "disallow `require` calls to be mixed with regular variable declarations",
18 category: "Node.js and CommonJS",
20 url: "https://eslint.org/docs/rules/no-mixed-requires"
39 additionalProperties: false
48 const options = context.options[0];
52 if (typeof options === "object") {
53 grouping = options.grouping;
54 allowCall = options.allowCall;
60 * Returns the list of built-in modules.
61 * @returns {string[]} An array of built-in Node.js modules.
63 function getBuiltinModules() {
66 * This list is generated using:
67 * `require("repl")._builtinLibs.concat('repl').sort()`
68 * This particular list is as per nodejs v0.12.2 and iojs v0.7.1
71 "assert", "buffer", "child_process", "cluster", "crypto",
72 "dgram", "dns", "domain", "events", "fs", "http", "https",
73 "net", "os", "path", "punycode", "querystring", "readline",
74 "repl", "smalloc", "stream", "string_decoder", "tls", "tty",
75 "url", "util", "v8", "vm", "zlib"
79 const BUILTIN_MODULES = getBuiltinModules();
81 const DECL_REQUIRE = "require",
82 DECL_UNINITIALIZED = "uninitialized",
85 const REQ_CORE = "core",
87 REQ_MODULE = "module",
88 REQ_COMPUTED = "computed";
91 * Determines the type of a declaration statement.
92 * @param {ASTNode} initExpression The init node of the VariableDeclarator.
93 * @returns {string} The type of declaration represented by the expression.
95 function getDeclarationType(initExpression) {
96 if (!initExpression) {
99 return DECL_UNINITIALIZED;
102 if (initExpression.type === "CallExpression" &&
103 initExpression.callee.type === "Identifier" &&
104 initExpression.callee.name === "require"
107 // "var x = require('util');"
111 initExpression.type === "CallExpression" &&
112 initExpression.callee.type === "CallExpression"
115 // "var x = require('diagnose')('sub-module');"
116 return getDeclarationType(initExpression.callee);
118 if (initExpression.type === "MemberExpression") {
120 // "var x = require('glob').Glob;"
121 return getDeclarationType(initExpression.object);
129 * Determines the type of module that is loaded via require.
130 * @param {ASTNode} initExpression The init node of the VariableDeclarator.
131 * @returns {string} The module type.
133 function inferModuleType(initExpression) {
134 if (initExpression.type === "MemberExpression") {
136 // "var x = require('glob').Glob;"
137 return inferModuleType(initExpression.object);
139 if (initExpression.arguments.length === 0) {
141 // "var x = require();"
145 const arg = initExpression.arguments[0];
147 if (arg.type !== "Literal" || typeof arg.value !== "string") {
149 // "var x = require(42);"
153 if (BUILTIN_MODULES.indexOf(arg.value) !== -1) {
155 // "var fs = require('fs');"
158 if (/^\.{0,2}\//u.test(arg.value)) {
160 // "var utils = require('./utils');"
164 // "var async = require('async');"
170 * Check if the list of variable declarations is mixed, i.e. whether it
171 * contains both require and other declarations.
172 * @param {ASTNode} declarations The list of VariableDeclarators.
173 * @returns {boolean} True if the declarations are mixed, false if not.
175 function isMixed(declarations) {
178 declarations.forEach(declaration => {
179 const type = getDeclarationType(declaration.init);
181 contains[type] = true;
185 contains[DECL_REQUIRE] &&
186 (contains[DECL_UNINITIALIZED] || contains[DECL_OTHER])
191 * Check if all require declarations in the given list are of the same
193 * @param {ASTNode} declarations The list of VariableDeclarators.
194 * @returns {boolean} True if the declarations are grouped, false if not.
196 function isGrouped(declarations) {
199 declarations.forEach(declaration => {
200 if (getDeclarationType(declaration.init) === DECL_REQUIRE) {
201 found[inferModuleType(declaration.init)] = true;
205 return Object.keys(found).length <= 1;
211 VariableDeclaration(node) {
213 if (isMixed(node.declarations)) {
214 context.report({ node, message: "Do not mix 'require' and other declarations." });
215 } else if (grouping && !isGrouped(node.declarations)) {
216 context.report({ node, message: "Do not mix core, module, file and computed requires." });