UNPKG

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