no-restricted-modules.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /**
  2. * @fileoverview Restrict usage of specified node modules.
  3. * @author Christian Schulz
  4. * @deprecated in ESLint v7.0.0
  5. */
  6. "use strict";
  7. //------------------------------------------------------------------------------
  8. // Rule Definition
  9. //------------------------------------------------------------------------------
  10. const ignore = require("ignore");
  11. const arrayOfStrings = {
  12. type: "array",
  13. items: { type: "string" },
  14. uniqueItems: true
  15. };
  16. const arrayOfStringsOrObjects = {
  17. type: "array",
  18. items: {
  19. anyOf: [
  20. { type: "string" },
  21. {
  22. type: "object",
  23. properties: {
  24. name: { type: "string" },
  25. message: {
  26. type: "string",
  27. minLength: 1
  28. }
  29. },
  30. additionalProperties: false,
  31. required: ["name"]
  32. }
  33. ]
  34. },
  35. uniqueItems: true
  36. };
  37. /** @type {import('../shared/types').Rule} */
  38. module.exports = {
  39. meta: {
  40. deprecated: true,
  41. replacedBy: [],
  42. type: "suggestion",
  43. docs: {
  44. description: "Disallow specified modules when loaded by `require`",
  45. recommended: false,
  46. url: "https://eslint.org/docs/rules/no-restricted-modules"
  47. },
  48. schema: {
  49. anyOf: [
  50. arrayOfStringsOrObjects,
  51. {
  52. type: "array",
  53. items: {
  54. type: "object",
  55. properties: {
  56. paths: arrayOfStringsOrObjects,
  57. patterns: arrayOfStrings
  58. },
  59. additionalProperties: false
  60. },
  61. additionalItems: false
  62. }
  63. ]
  64. },
  65. messages: {
  66. defaultMessage: "'{{name}}' module is restricted from being used.",
  67. // eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
  68. customMessage: "'{{name}}' module is restricted from being used. {{customMessage}}",
  69. patternMessage: "'{{name}}' module is restricted from being used by a pattern."
  70. }
  71. },
  72. create(context) {
  73. const options = Array.isArray(context.options) ? context.options : [];
  74. const isPathAndPatternsObject =
  75. typeof options[0] === "object" &&
  76. (Object.prototype.hasOwnProperty.call(options[0], "paths") || Object.prototype.hasOwnProperty.call(options[0], "patterns"));
  77. const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
  78. const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
  79. const restrictedPathMessages = restrictedPaths.reduce((memo, importName) => {
  80. if (typeof importName === "string") {
  81. memo[importName] = null;
  82. } else {
  83. memo[importName.name] = importName.message;
  84. }
  85. return memo;
  86. }, {});
  87. // if no imports are restricted we don't need to check
  88. if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
  89. return {};
  90. }
  91. // relative paths are supported for this rule
  92. const ig = ignore({ allowRelativePaths: true }).add(restrictedPatterns);
  93. /**
  94. * Function to check if a node is a string literal.
  95. * @param {ASTNode} node The node to check.
  96. * @returns {boolean} If the node is a string literal.
  97. */
  98. function isStringLiteral(node) {
  99. return node && node.type === "Literal" && typeof node.value === "string";
  100. }
  101. /**
  102. * Function to check if a node is a static string template literal.
  103. * @param {ASTNode} node The node to check.
  104. * @returns {boolean} If the node is a string template literal.
  105. */
  106. function isStaticTemplateLiteral(node) {
  107. return node && node.type === "TemplateLiteral" && node.expressions.length === 0;
  108. }
  109. /**
  110. * Function to check if a node is a require call.
  111. * @param {ASTNode} node The node to check.
  112. * @returns {boolean} If the node is a require call.
  113. */
  114. function isRequireCall(node) {
  115. return node.callee.type === "Identifier" && node.callee.name === "require";
  116. }
  117. /**
  118. * Extract string from Literal or TemplateLiteral node
  119. * @param {ASTNode} node The node to extract from
  120. * @returns {string|null} Extracted string or null if node doesn't represent a string
  121. */
  122. function getFirstArgumentString(node) {
  123. if (isStringLiteral(node)) {
  124. return node.value.trim();
  125. }
  126. if (isStaticTemplateLiteral(node)) {
  127. return node.quasis[0].value.cooked.trim();
  128. }
  129. return null;
  130. }
  131. /**
  132. * Report a restricted path.
  133. * @param {node} node representing the restricted path reference
  134. * @param {string} name restricted path
  135. * @returns {void}
  136. * @private
  137. */
  138. function reportPath(node, name) {
  139. const customMessage = restrictedPathMessages[name];
  140. const messageId = customMessage
  141. ? "customMessage"
  142. : "defaultMessage";
  143. context.report({
  144. node,
  145. messageId,
  146. data: {
  147. name,
  148. customMessage
  149. }
  150. });
  151. }
  152. /**
  153. * Check if the given name is a restricted path name
  154. * @param {string} name name of a variable
  155. * @returns {boolean} whether the variable is a restricted path or not
  156. * @private
  157. */
  158. function isRestrictedPath(name) {
  159. return Object.prototype.hasOwnProperty.call(restrictedPathMessages, name);
  160. }
  161. return {
  162. CallExpression(node) {
  163. if (isRequireCall(node)) {
  164. // node has arguments
  165. if (node.arguments.length) {
  166. const name = getFirstArgumentString(node.arguments[0]);
  167. // if first argument is a string literal or a static string template literal
  168. if (name) {
  169. // check if argument value is in restricted modules array
  170. if (isRestrictedPath(name)) {
  171. reportPath(node, name);
  172. }
  173. if (restrictedPatterns.length > 0 && ig.ignores(name)) {
  174. context.report({
  175. node,
  176. messageId: "patternMessage",
  177. data: { name }
  178. });
  179. }
  180. }
  181. }
  182. }
  183. }
  184. };
  185. }
  186. };