id-blacklist.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /**
  2. * @fileoverview Rule that warns when identifier names that are
  3. * specified in the configuration are used.
  4. * @author Keith Cirkel (http://keithcirkel.co.uk)
  5. * @deprecated in ESLint v7.5.0
  6. */
  7. "use strict";
  8. //------------------------------------------------------------------------------
  9. // Helpers
  10. //------------------------------------------------------------------------------
  11. /**
  12. * Checks whether the given node represents assignment target in a normal assignment or destructuring.
  13. * @param {ASTNode} node The node to check.
  14. * @returns {boolean} `true` if the node is assignment target.
  15. */
  16. function isAssignmentTarget(node) {
  17. const parent = node.parent;
  18. return (
  19. // normal assignment
  20. (
  21. parent.type === "AssignmentExpression" &&
  22. parent.left === node
  23. ) ||
  24. // destructuring
  25. parent.type === "ArrayPattern" ||
  26. parent.type === "RestElement" ||
  27. (
  28. parent.type === "Property" &&
  29. parent.value === node &&
  30. parent.parent.type === "ObjectPattern"
  31. ) ||
  32. (
  33. parent.type === "AssignmentPattern" &&
  34. parent.left === node
  35. )
  36. );
  37. }
  38. /**
  39. * Checks whether the given node represents an imported name that is renamed in the same import/export specifier.
  40. *
  41. * Examples:
  42. * import { a as b } from 'mod'; // node `a` is renamed import
  43. * export { a as b } from 'mod'; // node `a` is renamed import
  44. * @param {ASTNode} node `Identifier` node to check.
  45. * @returns {boolean} `true` if the node is a renamed import.
  46. */
  47. function isRenamedImport(node) {
  48. const parent = node.parent;
  49. return (
  50. (
  51. parent.type === "ImportSpecifier" &&
  52. parent.imported !== parent.local &&
  53. parent.imported === node
  54. ) ||
  55. (
  56. parent.type === "ExportSpecifier" &&
  57. parent.parent.source && // re-export
  58. parent.local !== parent.exported &&
  59. parent.local === node
  60. )
  61. );
  62. }
  63. /**
  64. * Checks whether the given node is a renamed identifier node in an ObjectPattern destructuring.
  65. *
  66. * Examples:
  67. * const { a : b } = foo; // node `a` is renamed node.
  68. * @param {ASTNode} node `Identifier` node to check.
  69. * @returns {boolean} `true` if the node is a renamed node in an ObjectPattern destructuring.
  70. */
  71. function isRenamedInDestructuring(node) {
  72. const parent = node.parent;
  73. return (
  74. (
  75. !parent.computed &&
  76. parent.type === "Property" &&
  77. parent.parent.type === "ObjectPattern" &&
  78. parent.value !== node &&
  79. parent.key === node
  80. )
  81. );
  82. }
  83. /**
  84. * Checks whether the given node represents shorthand definition of a property in an object literal.
  85. * @param {ASTNode} node `Identifier` node to check.
  86. * @returns {boolean} `true` if the node is a shorthand property definition.
  87. */
  88. function isShorthandPropertyDefinition(node) {
  89. const parent = node.parent;
  90. return (
  91. parent.type === "Property" &&
  92. parent.parent.type === "ObjectExpression" &&
  93. parent.shorthand
  94. );
  95. }
  96. //------------------------------------------------------------------------------
  97. // Rule Definition
  98. //------------------------------------------------------------------------------
  99. /** @type {import('../shared/types').Rule} */
  100. module.exports = {
  101. meta: {
  102. deprecated: true,
  103. replacedBy: ["id-denylist"],
  104. type: "suggestion",
  105. docs: {
  106. description: "Disallow specified identifiers",
  107. recommended: false,
  108. url: "https://eslint.org/docs/rules/id-blacklist"
  109. },
  110. schema: {
  111. type: "array",
  112. items: {
  113. type: "string"
  114. },
  115. uniqueItems: true
  116. },
  117. messages: {
  118. restricted: "Identifier '{{name}}' is restricted."
  119. }
  120. },
  121. create(context) {
  122. const denyList = new Set(context.options);
  123. const reportedNodes = new Set();
  124. let globalScope;
  125. /**
  126. * Checks whether the given name is restricted.
  127. * @param {string} name The name to check.
  128. * @returns {boolean} `true` if the name is restricted.
  129. * @private
  130. */
  131. function isRestricted(name) {
  132. return denyList.has(name);
  133. }
  134. /**
  135. * Checks whether the given node represents a reference to a global variable that is not declared in the source code.
  136. * These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
  137. * @param {ASTNode} node `Identifier` node to check.
  138. * @returns {boolean} `true` if the node is a reference to a global variable.
  139. */
  140. function isReferenceToGlobalVariable(node) {
  141. const variable = globalScope.set.get(node.name);
  142. return variable && variable.defs.length === 0 &&
  143. variable.references.some(ref => ref.identifier === node);
  144. }
  145. /**
  146. * Determines whether the given node should be checked.
  147. * @param {ASTNode} node `Identifier` node.
  148. * @returns {boolean} `true` if the node should be checked.
  149. */
  150. function shouldCheck(node) {
  151. const parent = node.parent;
  152. /*
  153. * Member access has special rules for checking property names.
  154. * Read access to a property with a restricted name is allowed, because it can be on an object that user has no control over.
  155. * Write access isn't allowed, because it potentially creates a new property with a restricted name.
  156. */
  157. if (
  158. parent.type === "MemberExpression" &&
  159. parent.property === node &&
  160. !parent.computed
  161. ) {
  162. return isAssignmentTarget(parent);
  163. }
  164. return (
  165. parent.type !== "CallExpression" &&
  166. parent.type !== "NewExpression" &&
  167. !isRenamedImport(node) &&
  168. !isRenamedInDestructuring(node) &&
  169. !(
  170. isReferenceToGlobalVariable(node) &&
  171. !isShorthandPropertyDefinition(node)
  172. )
  173. );
  174. }
  175. /**
  176. * Reports an AST node as a rule violation.
  177. * @param {ASTNode} node The node to report.
  178. * @returns {void}
  179. * @private
  180. */
  181. function report(node) {
  182. /*
  183. * We used the range instead of the node because it's possible
  184. * for the same identifier to be represented by two different
  185. * nodes, with the most clear example being shorthand properties:
  186. * { foo }
  187. * In this case, "foo" is represented by one node for the name
  188. * and one for the value. The only way to know they are the same
  189. * is to look at the range.
  190. */
  191. if (!reportedNodes.has(node.range.toString())) {
  192. context.report({
  193. node,
  194. messageId: "restricted",
  195. data: {
  196. name: node.name
  197. }
  198. });
  199. reportedNodes.add(node.range.toString());
  200. }
  201. }
  202. return {
  203. Program() {
  204. globalScope = context.getScope();
  205. },
  206. Identifier(node) {
  207. if (isRestricted(node.name) && shouldCheck(node)) {
  208. report(node);
  209. }
  210. }
  211. };
  212. }
  213. };