2 * @fileoverview Rule to enforce grouped require statements for Node.JS
3 * @author Raphael Pigulla
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
21 description: "disallow `require` calls to be mixed with regular variable declarations",
22 category: "Node.js and CommonJS",
24 url: "https://eslint.org/docs/rules/no-mixed-requires"
43 additionalProperties: false
50 noMixRequire: "Do not mix 'require' and other declarations.",
51 noMixCoreModuleFileComputed: "Do not mix core, module, file and computed requires."
57 const options = context.options[0];
61 if (typeof options === "object") {
62 grouping = options.grouping;
63 allowCall = options.allowCall;
69 * Returns the list of built-in modules.
70 * @returns {string[]} An array of built-in Node.js modules.
72 function getBuiltinModules() {
75 * This list is generated using:
76 * `require("repl")._builtinLibs.concat('repl').sort()`
77 * This particular list is as per nodejs v0.12.2 and iojs v0.7.1
80 "assert", "buffer", "child_process", "cluster", "crypto",
81 "dgram", "dns", "domain", "events", "fs", "http", "https",
82 "net", "os", "path", "punycode", "querystring", "readline",
83 "repl", "smalloc", "stream", "string_decoder", "tls", "tty",
84 "url", "util", "v8", "vm", "zlib"
88 const BUILTIN_MODULES = getBuiltinModules();
90 const DECL_REQUIRE = "require",
91 DECL_UNINITIALIZED = "uninitialized",
94 const REQ_CORE = "core",
96 REQ_MODULE = "module",
97 REQ_COMPUTED = "computed";
100 * Determines the type of a declaration statement.
101 * @param {ASTNode} initExpression The init node of the VariableDeclarator.
102 * @returns {string} The type of declaration represented by the expression.
104 function getDeclarationType(initExpression) {
105 if (!initExpression) {
108 return DECL_UNINITIALIZED;
111 if (initExpression.type === "CallExpression" &&
112 initExpression.callee.type === "Identifier" &&
113 initExpression.callee.name === "require"
116 // "var x = require('util');"
120 initExpression.type === "CallExpression" &&
121 initExpression.callee.type === "CallExpression"
124 // "var x = require('diagnose')('sub-module');"
125 return getDeclarationType(initExpression.callee);
127 if (initExpression.type === "MemberExpression") {
129 // "var x = require('glob').Glob;"
130 return getDeclarationType(initExpression.object);
138 * Determines the type of module that is loaded via require.
139 * @param {ASTNode} initExpression The init node of the VariableDeclarator.
140 * @returns {string} The module type.
142 function inferModuleType(initExpression) {
143 if (initExpression.type === "MemberExpression") {
145 // "var x = require('glob').Glob;"
146 return inferModuleType(initExpression.object);
148 if (initExpression.arguments.length === 0) {
150 // "var x = require();"
154 const arg = initExpression.arguments[0];
156 if (arg.type !== "Literal" || typeof arg.value !== "string") {
158 // "var x = require(42);"
162 if (BUILTIN_MODULES.indexOf(arg.value) !== -1) {
164 // "var fs = require('fs');"
167 if (/^\.{0,2}\//u.test(arg.value)) {
169 // "var utils = require('./utils');"
173 // "var async = require('async');"
179 * Check if the list of variable declarations is mixed, i.e. whether it
180 * contains both require and other declarations.
181 * @param {ASTNode} declarations The list of VariableDeclarators.
182 * @returns {boolean} True if the declarations are mixed, false if not.
184 function isMixed(declarations) {
187 declarations.forEach(declaration => {
188 const type = getDeclarationType(declaration.init);
190 contains[type] = true;
194 contains[DECL_REQUIRE] &&
195 (contains[DECL_UNINITIALIZED] || contains[DECL_OTHER])
200 * Check if all require declarations in the given list are of the same
202 * @param {ASTNode} declarations The list of VariableDeclarators.
203 * @returns {boolean} True if the declarations are grouped, false if not.
205 function isGrouped(declarations) {
208 declarations.forEach(declaration => {
209 if (getDeclarationType(declaration.init) === DECL_REQUIRE) {
210 found[inferModuleType(declaration.init)] = true;
214 return Object.keys(found).length <= 1;
220 VariableDeclaration(node) {
222 if (isMixed(node.declarations)) {
225 messageId: "noMixRequire"
227 } else if (grouping && !isGrouped(node.declarations)) {
230 messageId: "noMixCoreModuleFileComputed"