no-mixed-requires.js 7.3 KB

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