]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-mixed-requires.js
8e988e32c24f84780fb48bde5841ac525c070b10
[pve-eslint.git] / eslint / lib / rules / no-mixed-requires.js
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
12 module.exports = {
13 meta: {
14 type: "suggestion",
15
16 docs: {
17 description: "disallow `require` calls to be mixed with regular variable declarations",
18 category: "Node.js and CommonJS",
19 recommended: false,
20 url: "https://eslint.org/docs/rules/no-mixed-requires"
21 },
22
23 schema: [
24 {
25 oneOf: [
26 {
27 type: "boolean"
28 },
29 {
30 type: "object",
31 properties: {
32 grouping: {
33 type: "boolean"
34 },
35 allowCall: {
36 type: "boolean"
37 }
38 },
39 additionalProperties: false
40 }
41 ]
42 }
43 ],
44
45 messages: {
46 noMixRequire: "Do not mix 'require' and other declarations.",
47 noMixCoreModuleFileComputed: "Do not mix core, module, file and computed requires."
48 }
49 },
50
51 create(context) {
52
53 const options = context.options[0];
54 let grouping = false,
55 allowCall = false;
56
57 if (typeof options === "object") {
58 grouping = options.grouping;
59 allowCall = options.allowCall;
60 } else {
61 grouping = !!options;
62 }
63
64 /**
65 * Returns the list of built-in modules.
66 * @returns {string[]} An array of built-in Node.js modules.
67 */
68 function getBuiltinModules() {
69
70 /*
71 * This list is generated using:
72 * `require("repl")._builtinLibs.concat('repl').sort()`
73 * This particular list is as per nodejs v0.12.2 and iojs v0.7.1
74 */
75 return [
76 "assert", "buffer", "child_process", "cluster", "crypto",
77 "dgram", "dns", "domain", "events", "fs", "http", "https",
78 "net", "os", "path", "punycode", "querystring", "readline",
79 "repl", "smalloc", "stream", "string_decoder", "tls", "tty",
80 "url", "util", "v8", "vm", "zlib"
81 ];
82 }
83
84 const BUILTIN_MODULES = getBuiltinModules();
85
86 const DECL_REQUIRE = "require",
87 DECL_UNINITIALIZED = "uninitialized",
88 DECL_OTHER = "other";
89
90 const REQ_CORE = "core",
91 REQ_FILE = "file",
92 REQ_MODULE = "module",
93 REQ_COMPUTED = "computed";
94
95 /**
96 * Determines the type of a declaration statement.
97 * @param {ASTNode} initExpression The init node of the VariableDeclarator.
98 * @returns {string} The type of declaration represented by the expression.
99 */
100 function getDeclarationType(initExpression) {
101 if (!initExpression) {
102
103 // "var x;"
104 return DECL_UNINITIALIZED;
105 }
106
107 if (initExpression.type === "CallExpression" &&
108 initExpression.callee.type === "Identifier" &&
109 initExpression.callee.name === "require"
110 ) {
111
112 // "var x = require('util');"
113 return DECL_REQUIRE;
114 }
115 if (allowCall &&
116 initExpression.type === "CallExpression" &&
117 initExpression.callee.type === "CallExpression"
118 ) {
119
120 // "var x = require('diagnose')('sub-module');"
121 return getDeclarationType(initExpression.callee);
122 }
123 if (initExpression.type === "MemberExpression") {
124
125 // "var x = require('glob').Glob;"
126 return getDeclarationType(initExpression.object);
127 }
128
129 // "var x = 42;"
130 return DECL_OTHER;
131 }
132
133 /**
134 * Determines the type of module that is loaded via require.
135 * @param {ASTNode} initExpression The init node of the VariableDeclarator.
136 * @returns {string} The module type.
137 */
138 function inferModuleType(initExpression) {
139 if (initExpression.type === "MemberExpression") {
140
141 // "var x = require('glob').Glob;"
142 return inferModuleType(initExpression.object);
143 }
144 if (initExpression.arguments.length === 0) {
145
146 // "var x = require();"
147 return REQ_COMPUTED;
148 }
149
150 const arg = initExpression.arguments[0];
151
152 if (arg.type !== "Literal" || typeof arg.value !== "string") {
153
154 // "var x = require(42);"
155 return REQ_COMPUTED;
156 }
157
158 if (BUILTIN_MODULES.indexOf(arg.value) !== -1) {
159
160 // "var fs = require('fs');"
161 return REQ_CORE;
162 }
163 if (/^\.{0,2}\//u.test(arg.value)) {
164
165 // "var utils = require('./utils');"
166 return REQ_FILE;
167 }
168
169 // "var async = require('async');"
170 return REQ_MODULE;
171
172 }
173
174 /**
175 * Check if the list of variable declarations is mixed, i.e. whether it
176 * contains both require and other declarations.
177 * @param {ASTNode} declarations The list of VariableDeclarators.
178 * @returns {boolean} True if the declarations are mixed, false if not.
179 */
180 function isMixed(declarations) {
181 const contains = {};
182
183 declarations.forEach(declaration => {
184 const type = getDeclarationType(declaration.init);
185
186 contains[type] = true;
187 });
188
189 return !!(
190 contains[DECL_REQUIRE] &&
191 (contains[DECL_UNINITIALIZED] || contains[DECL_OTHER])
192 );
193 }
194
195 /**
196 * Check if all require declarations in the given list are of the same
197 * type.
198 * @param {ASTNode} declarations The list of VariableDeclarators.
199 * @returns {boolean} True if the declarations are grouped, false if not.
200 */
201 function isGrouped(declarations) {
202 const found = {};
203
204 declarations.forEach(declaration => {
205 if (getDeclarationType(declaration.init) === DECL_REQUIRE) {
206 found[inferModuleType(declaration.init)] = true;
207 }
208 });
209
210 return Object.keys(found).length <= 1;
211 }
212
213
214 return {
215
216 VariableDeclaration(node) {
217
218 if (isMixed(node.declarations)) {
219 context.report({
220 node,
221 messageId: "noMixRequire"
222 });
223 } else if (grouping && !isGrouped(node.declarations)) {
224 context.report({
225 node,
226 messageId: "noMixCoreModuleFileComputed"
227 });
228 }
229 }
230 };
231
232 }
233 };