id-length.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /**
  2. * @fileoverview Rule that warns when identifier names are shorter or longer
  3. * than the values provided in configuration.
  4. * @author Burak Yigit Kaya aka BYK
  5. */
  6. "use strict";
  7. //------------------------------------------------------------------------------
  8. // Rule Definition
  9. //------------------------------------------------------------------------------
  10. module.exports = {
  11. meta: {
  12. type: "suggestion",
  13. docs: {
  14. description: "enforce minimum and maximum identifier lengths",
  15. category: "Stylistic Issues",
  16. recommended: false,
  17. url: "https://eslint.org/docs/rules/id-length"
  18. },
  19. schema: [
  20. {
  21. type: "object",
  22. properties: {
  23. min: {
  24. type: "integer",
  25. default: 2
  26. },
  27. max: {
  28. type: "integer"
  29. },
  30. exceptions: {
  31. type: "array",
  32. uniqueItems: true,
  33. items: {
  34. type: "string"
  35. }
  36. },
  37. exceptionPatterns: {
  38. type: "array",
  39. uniqueItems: true,
  40. items: {
  41. type: "string"
  42. }
  43. },
  44. properties: {
  45. enum: ["always", "never"]
  46. }
  47. },
  48. additionalProperties: false
  49. }
  50. ],
  51. messages: {
  52. tooShort: "Identifier name '{{name}}' is too short (< {{min}}).",
  53. tooLong: "Identifier name '{{name}}' is too long (> {{max}})."
  54. }
  55. },
  56. create(context) {
  57. const options = context.options[0] || {};
  58. const minLength = typeof options.min !== "undefined" ? options.min : 2;
  59. const maxLength = typeof options.max !== "undefined" ? options.max : Infinity;
  60. const properties = options.properties !== "never";
  61. const exceptions = new Set(options.exceptions);
  62. const exceptionPatterns = (options.exceptionPatterns || []).map(pattern => new RegExp(pattern, "u"));
  63. const reportedNode = new Set();
  64. /**
  65. * Checks if a string matches the provided exception patterns
  66. * @param {string} name The string to check.
  67. * @returns {boolean} if the string is a match
  68. * @private
  69. */
  70. function matchesExceptionPattern(name) {
  71. return exceptionPatterns.some(pattern => pattern.test(name));
  72. }
  73. const SUPPORTED_EXPRESSIONS = {
  74. MemberExpression: properties && function(parent) {
  75. return !parent.computed && (
  76. // regular property assignment
  77. (parent.parent.left === parent && parent.parent.type === "AssignmentExpression" ||
  78. // or the last identifier in an ObjectPattern destructuring
  79. parent.parent.type === "Property" && parent.parent.value === parent &&
  80. parent.parent.parent.type === "ObjectPattern" && parent.parent.parent.parent.left === parent.parent.parent)
  81. );
  82. },
  83. AssignmentPattern(parent, node) {
  84. return parent.left === node;
  85. },
  86. VariableDeclarator(parent, node) {
  87. return parent.id === node;
  88. },
  89. Property(parent, node) {
  90. if (parent.parent.type === "ObjectPattern") {
  91. return (
  92. parent.value !== parent.key && parent.value === node ||
  93. parent.value === parent.key && parent.key === node && properties
  94. );
  95. }
  96. return properties && !parent.computed && parent.key === node;
  97. },
  98. ImportDefaultSpecifier: true,
  99. RestElement: true,
  100. FunctionExpression: true,
  101. ArrowFunctionExpression: true,
  102. ClassDeclaration: true,
  103. FunctionDeclaration: true,
  104. MethodDefinition: true,
  105. CatchClause: true,
  106. ArrayPattern: true
  107. };
  108. return {
  109. Identifier(node) {
  110. const name = node.name;
  111. const parent = node.parent;
  112. const isShort = name.length < minLength;
  113. const isLong = name.length > maxLength;
  114. if (!(isShort || isLong) || exceptions.has(name) || matchesExceptionPattern(name)) {
  115. return; // Nothing to report
  116. }
  117. const isValidExpression = SUPPORTED_EXPRESSIONS[parent.type];
  118. if (isValidExpression && !reportedNode.has(node) && (isValidExpression === true || isValidExpression(parent, node))) {
  119. reportedNode.add(node);
  120. context.report({
  121. node,
  122. messageId: isShort ? "tooShort" : "tooLong",
  123. data: { name, min: minLength, max: maxLength }
  124. });
  125. }
  126. }
  127. };
  128. }
  129. };