no-mixed-requires.js 6.2 KB

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