1 | /**
|
---|
2 | * @fileoverview Rule to enforce grouped require statements for Node.JS
|
---|
3 | * @author Raphael Pigulla
|
---|
4 | * @deprecated in ESLint v7.0.0
|
---|
5 | */
|
---|
6 |
|
---|
7 | "use strict";
|
---|
8 |
|
---|
9 | //------------------------------------------------------------------------------
|
---|
10 | // Rule Definition
|
---|
11 | //------------------------------------------------------------------------------
|
---|
12 |
|
---|
13 | /** @type {import('../shared/types').Rule} */
|
---|
14 | module.exports = {
|
---|
15 | meta: {
|
---|
16 | deprecated: true,
|
---|
17 |
|
---|
18 | replacedBy: [],
|
---|
19 |
|
---|
20 | type: "suggestion",
|
---|
21 |
|
---|
22 | docs: {
|
---|
23 | description: "Disallow `require` calls to be mixed with regular variable declarations",
|
---|
24 | recommended: false,
|
---|
25 | url: "https://eslint.org/docs/latest/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.includes(arg.value)) {
|
---|
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 | };
|
---|