no-underscore-dangle.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /**
  2. * @fileoverview Rule to flag trailing underscores in variable declarations.
  3. * @author Matt DuVall <http://www.mattduvall.com>
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Rule Definition
  8. //------------------------------------------------------------------------------
  9. module.exports = {
  10. meta: {
  11. docs: {
  12. description: "disallow dangling underscores in identifiers",
  13. category: "Stylistic Issues",
  14. recommended: false,
  15. url: "https://eslint.org/docs/rules/no-underscore-dangle"
  16. },
  17. schema: [
  18. {
  19. type: "object",
  20. properties: {
  21. allow: {
  22. type: "array",
  23. items: {
  24. type: "string"
  25. }
  26. },
  27. allowAfterThis: {
  28. type: "boolean"
  29. },
  30. allowAfterSuper: {
  31. type: "boolean"
  32. },
  33. enforceInMethodNames: {
  34. type: "boolean"
  35. }
  36. },
  37. additionalProperties: false
  38. }
  39. ]
  40. },
  41. create(context) {
  42. const options = context.options[0] || {};
  43. const ALLOWED_VARIABLES = options.allow ? options.allow : [];
  44. const allowAfterThis = typeof options.allowAfterThis !== "undefined" ? options.allowAfterThis : false;
  45. const allowAfterSuper = typeof options.allowAfterSuper !== "undefined" ? options.allowAfterSuper : false;
  46. const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false;
  47. //-------------------------------------------------------------------------
  48. // Helpers
  49. //-------------------------------------------------------------------------
  50. /**
  51. * Check if identifier is present inside the allowed option
  52. * @param {string} identifier name of the node
  53. * @returns {boolean} true if its is present
  54. * @private
  55. */
  56. function isAllowed(identifier) {
  57. return ALLOWED_VARIABLES.some(ident => ident === identifier);
  58. }
  59. /**
  60. * Check if identifier has a underscore at the end
  61. * @param {ASTNode} identifier node to evaluate
  62. * @returns {boolean} true if its is present
  63. * @private
  64. */
  65. function hasTrailingUnderscore(identifier) {
  66. const len = identifier.length;
  67. return identifier !== "_" && (identifier[0] === "_" || identifier[len - 1] === "_");
  68. }
  69. /**
  70. * Check if identifier is a special case member expression
  71. * @param {ASTNode} identifier node to evaluate
  72. * @returns {boolean} true if its is a special case
  73. * @private
  74. */
  75. function isSpecialCaseIdentifierForMemberExpression(identifier) {
  76. return identifier === "__proto__";
  77. }
  78. /**
  79. * Check if identifier is a special case variable expression
  80. * @param {ASTNode} identifier node to evaluate
  81. * @returns {boolean} true if its is a special case
  82. * @private
  83. */
  84. function isSpecialCaseIdentifierInVariableExpression(identifier) {
  85. // Checks for the underscore library usage here
  86. return identifier === "_";
  87. }
  88. /**
  89. * Check if function has a underscore at the end
  90. * @param {ASTNode} node node to evaluate
  91. * @returns {void}
  92. * @private
  93. */
  94. function checkForTrailingUnderscoreInFunctionDeclaration(node) {
  95. if (node.id) {
  96. const identifier = node.id.name;
  97. if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) && !isAllowed(identifier)) {
  98. context.report({
  99. node,
  100. message: "Unexpected dangling '_' in '{{identifier}}'.",
  101. data: {
  102. identifier
  103. }
  104. });
  105. }
  106. }
  107. }
  108. /**
  109. * Check if variable expression has a underscore at the end
  110. * @param {ASTNode} node node to evaluate
  111. * @returns {void}
  112. * @private
  113. */
  114. function checkForTrailingUnderscoreInVariableExpression(node) {
  115. const identifier = node.id.name;
  116. if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) &&
  117. !isSpecialCaseIdentifierInVariableExpression(identifier) && !isAllowed(identifier)) {
  118. context.report({
  119. node,
  120. message: "Unexpected dangling '_' in '{{identifier}}'.",
  121. data: {
  122. identifier
  123. }
  124. });
  125. }
  126. }
  127. /**
  128. * Check if member expression has a underscore at the end
  129. * @param {ASTNode} node node to evaluate
  130. * @returns {void}
  131. * @private
  132. */
  133. function checkForTrailingUnderscoreInMemberExpression(node) {
  134. const identifier = node.property.name,
  135. isMemberOfThis = node.object.type === "ThisExpression",
  136. isMemberOfSuper = node.object.type === "Super";
  137. if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) &&
  138. !(isMemberOfThis && allowAfterThis) &&
  139. !(isMemberOfSuper && allowAfterSuper) &&
  140. !isSpecialCaseIdentifierForMemberExpression(identifier) && !isAllowed(identifier)) {
  141. context.report({
  142. node,
  143. message: "Unexpected dangling '_' in '{{identifier}}'.",
  144. data: {
  145. identifier
  146. }
  147. });
  148. }
  149. }
  150. /**
  151. * Check if method declaration or method property has a underscore at the end
  152. * @param {ASTNode} node node to evaluate
  153. * @returns {void}
  154. * @private
  155. */
  156. function checkForTrailingUnderscoreInMethod(node) {
  157. const identifier = node.key.name;
  158. const isMethod = node.type === "MethodDefinition" || node.type === "Property" && node.method;
  159. if (typeof identifier !== "undefined" && enforceInMethodNames && isMethod && hasTrailingUnderscore(identifier)) {
  160. context.report({
  161. node,
  162. message: "Unexpected dangling '_' in '{{identifier}}'.",
  163. data: {
  164. identifier
  165. }
  166. });
  167. }
  168. }
  169. //--------------------------------------------------------------------------
  170. // Public API
  171. //--------------------------------------------------------------------------
  172. return {
  173. FunctionDeclaration: checkForTrailingUnderscoreInFunctionDeclaration,
  174. VariableDeclarator: checkForTrailingUnderscoreInVariableExpression,
  175. MemberExpression: checkForTrailingUnderscoreInMemberExpression,
  176. MethodDefinition: checkForTrailingUnderscoreInMethod,
  177. Property: checkForTrailingUnderscoreInMethod
  178. };
  179. }
  180. };