UNPKG

7.4 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to enforce grouped require statements for Node.JS
3 * @author Raphael Pigulla
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Rule Definition
10//------------------------------------------------------------------------------
11
12module.exports = {
13 meta: {
14 deprecated: true,
15
16 replacedBy: [],
17
18 type: "suggestion",
19
20 docs: {
21 description: "disallow `require` calls to be mixed with regular variable declarations",
22 category: "Node.js and CommonJS",
23 recommended: false,
24 url: "https://eslint.org/docs/rules/no-mixed-requires"
25 },
26
27 schema: [
28 {
29 oneOf: [
30 {
31 type: "boolean"
32 },
33 {
34 type: "object",
35 properties: {
36 grouping: {
37 type: "boolean"
38 },
39 allowCall: {
40 type: "boolean"
41 }
42 },
43 additionalProperties: false
44 }
45 ]
46 }
47 ],
48
49 messages: {
50 noMixRequire: "Do not mix 'require' and other declarations.",
51 noMixCoreModuleFileComputed: "Do not mix core, module, file and computed requires."
52 }
53 },
54
55 create(context) {
56
57 const options = context.options[0];
58 let grouping = false,
59 allowCall = false;
60
61 if (typeof options === "object") {
62 grouping = options.grouping;
63 allowCall = options.allowCall;
64 } else {
65 grouping = !!options;
66 }
67
68 /**
69 * Returns the list of built-in modules.
70 * @returns {string[]} An array of built-in Node.js modules.
71 */
72 function getBuiltinModules() {
73
74 /*
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
78 */
79 return [
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"
85 ];
86 }
87
88 const BUILTIN_MODULES = getBuiltinModules();
89
90 const DECL_REQUIRE = "require",
91 DECL_UNINITIALIZED = "uninitialized",
92 DECL_OTHER = "other";
93
94 const REQ_CORE = "core",
95 REQ_FILE = "file",
96 REQ_MODULE = "module",
97 REQ_COMPUTED = "computed";
98
99 /**
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.
103 */
104 function getDeclarationType(initExpression) {
105 if (!initExpression) {
106
107 // "var x;"
108 return DECL_UNINITIALIZED;
109 }
110
111 if (initExpression.type === "CallExpression" &&
112 initExpression.callee.type === "Identifier" &&
113 initExpression.callee.name === "require"
114 ) {
115
116 // "var x = require('util');"
117 return DECL_REQUIRE;
118 }
119 if (allowCall &&
120 initExpression.type === "CallExpression" &&
121 initExpression.callee.type === "CallExpression"
122 ) {
123
124 // "var x = require('diagnose')('sub-module');"
125 return getDeclarationType(initExpression.callee);
126 }
127 if (initExpression.type === "MemberExpression") {
128
129 // "var x = require('glob').Glob;"
130 return getDeclarationType(initExpression.object);
131 }
132
133 // "var x = 42;"
134 return DECL_OTHER;
135 }
136
137 /**
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.
141 */
142 function inferModuleType(initExpression) {
143 if (initExpression.type === "MemberExpression") {
144
145 // "var x = require('glob').Glob;"
146 return inferModuleType(initExpression.object);
147 }
148 if (initExpression.arguments.length === 0) {
149
150 // "var x = require();"
151 return REQ_COMPUTED;
152 }
153
154 const arg = initExpression.arguments[0];
155
156 if (arg.type !== "Literal" || typeof arg.value !== "string") {
157
158 // "var x = require(42);"
159 return REQ_COMPUTED;
160 }
161
162 if (BUILTIN_MODULES.indexOf(arg.value) !== -1) {
163
164 // "var fs = require('fs');"
165 return REQ_CORE;
166 }
167 if (/^\.{0,2}\//u.test(arg.value)) {
168
169 // "var utils = require('./utils');"
170 return REQ_FILE;
171 }
172
173 // "var async = require('async');"
174 return REQ_MODULE;
175
176 }
177
178 /**
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.
183 */
184 function isMixed(declarations) {
185 const contains = {};
186
187 declarations.forEach(declaration => {
188 const type = getDeclarationType(declaration.init);
189
190 contains[type] = true;
191 });
192
193 return !!(
194 contains[DECL_REQUIRE] &&
195 (contains[DECL_UNINITIALIZED] || contains[DECL_OTHER])
196 );
197 }
198
199 /**
200 * Check if all require declarations in the given list are of the same
201 * type.
202 * @param {ASTNode} declarations The list of VariableDeclarators.
203 * @returns {boolean} True if the declarations are grouped, false if not.
204 */
205 function isGrouped(declarations) {
206 const found = {};
207
208 declarations.forEach(declaration => {
209 if (getDeclarationType(declaration.init) === DECL_REQUIRE) {
210 found[inferModuleType(declaration.init)] = true;
211 }
212 });
213
214 return Object.keys(found).length <= 1;
215 }
216
217
218 return {
219
220 VariableDeclaration(node) {
221
222 if (isMixed(node.declarations)) {
223 context.report({
224 node,
225 messageId: "noMixRequire"
226 });
227 } else if (grouping && !isGrouped(node.declarations)) {
228 context.report({
229 node,
230 messageId: "noMixCoreModuleFileComputed"
231 });
232 }
233 }
234 };
235
236 }
237};