func-names.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. /**
  2. * @fileoverview Rule to warn when a function expression does not have a name.
  3. * @author Kyle T. Nunery
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const astUtils = require("../ast-utils");
  10. /**
  11. * Checks whether or not a given variable is a function name.
  12. * @param {eslint-scope.Variable} variable - A variable to check.
  13. * @returns {boolean} `true` if the variable is a function name.
  14. */
  15. function isFunctionName(variable) {
  16. return variable && variable.defs[0].type === "FunctionName";
  17. }
  18. //------------------------------------------------------------------------------
  19. // Rule Definition
  20. //------------------------------------------------------------------------------
  21. module.exports = {
  22. meta: {
  23. docs: {
  24. description: "require or disallow named `function` expressions",
  25. category: "Stylistic Issues",
  26. recommended: false,
  27. url: "https://eslint.org/docs/rules/func-names"
  28. },
  29. schema: [
  30. {
  31. enum: ["always", "as-needed", "never"]
  32. }
  33. ]
  34. },
  35. create(context) {
  36. const never = context.options[0] === "never";
  37. const asNeeded = context.options[0] === "as-needed";
  38. /**
  39. * Determines whether the current FunctionExpression node is a get, set, or
  40. * shorthand method in an object literal or a class.
  41. * @param {ASTNode} node - A node to check.
  42. * @returns {boolean} True if the node is a get, set, or shorthand method.
  43. */
  44. function isObjectOrClassMethod(node) {
  45. const parent = node.parent;
  46. return (parent.type === "MethodDefinition" || (
  47. parent.type === "Property" && (
  48. parent.method ||
  49. parent.kind === "get" ||
  50. parent.kind === "set"
  51. )
  52. ));
  53. }
  54. /**
  55. * Determines whether the current FunctionExpression node has a name that would be
  56. * inferred from context in a conforming ES6 environment.
  57. * @param {ASTNode} node - A node to check.
  58. * @returns {boolean} True if the node would have a name assigned automatically.
  59. */
  60. function hasInferredName(node) {
  61. const parent = node.parent;
  62. return isObjectOrClassMethod(node) ||
  63. (parent.type === "VariableDeclarator" && parent.id.type === "Identifier" && parent.init === node) ||
  64. (parent.type === "Property" && parent.value === node) ||
  65. (parent.type === "AssignmentExpression" && parent.left.type === "Identifier" && parent.right === node) ||
  66. (parent.type === "ExportDefaultDeclaration" && parent.declaration === node) ||
  67. (parent.type === "AssignmentPattern" && parent.right === node);
  68. }
  69. return {
  70. "FunctionExpression:exit"(node) {
  71. // Skip recursive functions.
  72. const nameVar = context.getDeclaredVariables(node)[0];
  73. if (isFunctionName(nameVar) && nameVar.references.length > 0) {
  74. return;
  75. }
  76. const hasName = Boolean(node.id && node.id.name);
  77. const name = astUtils.getFunctionNameWithKind(node);
  78. if (never) {
  79. if (hasName) {
  80. context.report({
  81. node,
  82. message: "Unexpected named {{name}}.",
  83. data: { name }
  84. });
  85. }
  86. } else {
  87. if (!hasName && (asNeeded ? !hasInferredName(node) : !isObjectOrClassMethod(node))) {
  88. context.report({
  89. node,
  90. message: "Unexpected unnamed {{name}}.",
  91. data: { name }
  92. });
  93. }
  94. }
  95. }
  96. };
  97. }
  98. };