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