no-mixed-requires.js 7.2 KB

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