Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

188 строки
6.9 KiB

  1. /**
  2. * @fileoverview Rule to flag non-camelcased identifiers
  3. * @author Nicholas C. Zakas
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Rule Definition
  8. //------------------------------------------------------------------------------
  9. module.exports = {
  10. meta: {
  11. docs: {
  12. description: "enforce camelcase naming convention",
  13. category: "Stylistic Issues",
  14. recommended: false,
  15. url: "https://eslint.org/docs/rules/camelcase"
  16. },
  17. schema: [
  18. {
  19. type: "object",
  20. properties: {
  21. ignoreDestructuring: {
  22. type: "boolean"
  23. },
  24. properties: {
  25. enum: ["always", "never"]
  26. }
  27. },
  28. additionalProperties: false
  29. }
  30. ],
  31. messages: {
  32. notCamelCase: "Identifier '{{name}}' is not in camel case."
  33. }
  34. },
  35. create(context) {
  36. //--------------------------------------------------------------------------
  37. // Helpers
  38. //--------------------------------------------------------------------------
  39. // contains reported nodes to avoid reporting twice on destructuring with shorthand notation
  40. const reported = [];
  41. const ALLOWED_PARENT_TYPES = new Set(["CallExpression", "NewExpression"]);
  42. /**
  43. * Checks if a string contains an underscore and isn't all upper-case
  44. * @param {string} name The string to check.
  45. * @returns {boolean} if the string is underscored
  46. * @private
  47. */
  48. function isUnderscored(name) {
  49. // if there's an underscore, it might be A_CONSTANT, which is okay
  50. return name.indexOf("_") > -1 && name !== name.toUpperCase();
  51. }
  52. /**
  53. * Checks if a parent of a node is an ObjectPattern.
  54. * @param {ASTNode} node The node to check.
  55. * @returns {boolean} if the node is inside an ObjectPattern
  56. * @private
  57. */
  58. function isInsideObjectPattern(node) {
  59. let { parent } = node;
  60. while (parent) {
  61. if (parent.type === "ObjectPattern") {
  62. return true;
  63. }
  64. parent = parent.parent;
  65. }
  66. return false;
  67. }
  68. /**
  69. * Reports an AST node as a rule violation.
  70. * @param {ASTNode} node The node to report.
  71. * @returns {void}
  72. * @private
  73. */
  74. function report(node) {
  75. if (reported.indexOf(node) < 0) {
  76. reported.push(node);
  77. context.report({ node, messageId: "notCamelCase", data: { name: node.name } });
  78. }
  79. }
  80. const options = context.options[0] || {};
  81. let properties = options.properties || "";
  82. const ignoreDestructuring = options.ignoreDestructuring || false;
  83. if (properties !== "always" && properties !== "never") {
  84. properties = "always";
  85. }
  86. return {
  87. Identifier(node) {
  88. /*
  89. * Leading and trailing underscores are commonly used to flag
  90. * private/protected identifiers, strip them
  91. */
  92. const name = node.name.replace(/^_+|_+$/g, ""),
  93. effectiveParent = (node.parent.type === "MemberExpression") ? node.parent.parent : node.parent;
  94. // MemberExpressions get special rules
  95. if (node.parent.type === "MemberExpression") {
  96. // "never" check properties
  97. if (properties === "never") {
  98. return;
  99. }
  100. // Always report underscored object names
  101. if (node.parent.object.type === "Identifier" && node.parent.object.name === node.name && isUnderscored(name)) {
  102. report(node);
  103. // Report AssignmentExpressions only if they are the left side of the assignment
  104. } else if (effectiveParent.type === "AssignmentExpression" && isUnderscored(name) && (effectiveParent.right.type !== "MemberExpression" || effectiveParent.left.type === "MemberExpression" && effectiveParent.left.property.name === node.name)) {
  105. report(node);
  106. }
  107. /*
  108. * Properties have their own rules, and
  109. * AssignmentPattern nodes can be treated like Properties:
  110. * e.g.: const { no_camelcased = false } = bar;
  111. */
  112. } else if (node.parent.type === "Property" || node.parent.type === "AssignmentPattern") {
  113. if (node.parent.parent && node.parent.parent.type === "ObjectPattern") {
  114. if (node.parent.shorthand && node.parent.value.left && isUnderscored(name)) {
  115. report(node);
  116. }
  117. const assignmentKeyEqualsValue = node.parent.key.name === node.parent.value.name;
  118. // prevent checking righthand side of destructured object
  119. if (node.parent.key === node && node.parent.value !== node) {
  120. return;
  121. }
  122. const valueIsUnderscored = node.parent.value.name && isUnderscored(name);
  123. // ignore destructuring if the option is set, unless a new identifier is created
  124. if (valueIsUnderscored && !(assignmentKeyEqualsValue && ignoreDestructuring)) {
  125. report(node);
  126. }
  127. }
  128. // "never" check properties or always ignore destructuring
  129. if (properties === "never" || (ignoreDestructuring && isInsideObjectPattern(node))) {
  130. return;
  131. }
  132. // don't check right hand side of AssignmentExpression to prevent duplicate warnings
  133. if (isUnderscored(name) && !ALLOWED_PARENT_TYPES.has(effectiveParent.type) && !(node.parent.right === node)) {
  134. report(node);
  135. }
  136. // Check if it's an import specifier
  137. } else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].indexOf(node.parent.type) >= 0) {
  138. // Report only if the local imported identifier is underscored
  139. if (node.parent.local && node.parent.local.name === node.name && isUnderscored(name)) {
  140. report(node);
  141. }
  142. // Report anything that is underscored that isn't a CallExpression
  143. } else if (isUnderscored(name) && !ALLOWED_PARENT_TYPES.has(effectiveParent.type)) {
  144. report(node);
  145. }
  146. }
  147. };
  148. }
  149. };