space-before-function-paren.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /**
  2. * @fileoverview Rule to validate spacing before function paren.
  3. * @author Mathias Schreck <https://github.com/lo1tuma>
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const astUtils = require("../ast-utils");
  10. //------------------------------------------------------------------------------
  11. // Rule Definition
  12. //------------------------------------------------------------------------------
  13. module.exports = {
  14. meta: {
  15. docs: {
  16. description: "enforce consistent spacing before `function` definition opening parenthesis",
  17. category: "Stylistic Issues",
  18. recommended: false,
  19. url: "https://eslint.org/docs/rules/space-before-function-paren"
  20. },
  21. fixable: "whitespace",
  22. schema: [
  23. {
  24. oneOf: [
  25. {
  26. enum: ["always", "never"]
  27. },
  28. {
  29. type: "object",
  30. properties: {
  31. anonymous: {
  32. enum: ["always", "never", "ignore"]
  33. },
  34. named: {
  35. enum: ["always", "never", "ignore"]
  36. },
  37. asyncArrow: {
  38. enum: ["always", "never", "ignore"]
  39. }
  40. },
  41. additionalProperties: false
  42. }
  43. ]
  44. }
  45. ]
  46. },
  47. create(context) {
  48. const sourceCode = context.getSourceCode();
  49. const baseConfig = typeof context.options[0] === "string" ? context.options[0] : "always";
  50. const overrideConfig = typeof context.options[0] === "object" ? context.options[0] : {};
  51. /**
  52. * Determines whether a function has a name.
  53. * @param {ASTNode} node The function node.
  54. * @returns {boolean} Whether the function has a name.
  55. */
  56. function isNamedFunction(node) {
  57. if (node.id) {
  58. return true;
  59. }
  60. const parent = node.parent;
  61. return parent.type === "MethodDefinition" ||
  62. (parent.type === "Property" &&
  63. (
  64. parent.kind === "get" ||
  65. parent.kind === "set" ||
  66. parent.method
  67. )
  68. );
  69. }
  70. /**
  71. * Gets the config for a given function
  72. * @param {ASTNode} node The function node
  73. * @returns {string} "always", "never", or "ignore"
  74. */
  75. function getConfigForFunction(node) {
  76. if (node.type === "ArrowFunctionExpression") {
  77. // Always ignore non-async functions and arrow functions without parens, e.g. async foo => bar
  78. if (node.async && astUtils.isOpeningParenToken(sourceCode.getFirstToken(node, { skip: 1 }))) {
  79. return overrideConfig.asyncArrow || baseConfig;
  80. }
  81. } else if (isNamedFunction(node)) {
  82. return overrideConfig.named || baseConfig;
  83. // `generator-star-spacing` should warn anonymous generators. E.g. `function* () {}`
  84. } else if (!node.generator) {
  85. return overrideConfig.anonymous || baseConfig;
  86. }
  87. return "ignore";
  88. }
  89. /**
  90. * Checks the parens of a function node
  91. * @param {ASTNode} node A function node
  92. * @returns {void}
  93. */
  94. function checkFunction(node) {
  95. const functionConfig = getConfigForFunction(node);
  96. if (functionConfig === "ignore") {
  97. return;
  98. }
  99. const rightToken = sourceCode.getFirstToken(node, astUtils.isOpeningParenToken);
  100. const leftToken = sourceCode.getTokenBefore(rightToken);
  101. const hasSpacing = sourceCode.isSpaceBetweenTokens(leftToken, rightToken);
  102. if (hasSpacing && functionConfig === "never") {
  103. context.report({
  104. node,
  105. loc: leftToken.loc.end,
  106. message: "Unexpected space before function parentheses.",
  107. fix: fixer => fixer.removeRange([leftToken.range[1], rightToken.range[0]])
  108. });
  109. } else if (!hasSpacing && functionConfig === "always") {
  110. context.report({
  111. node,
  112. loc: leftToken.loc.end,
  113. message: "Missing space before function parentheses.",
  114. fix: fixer => fixer.insertTextAfter(leftToken, " ")
  115. });
  116. }
  117. }
  118. return {
  119. ArrowFunctionExpression: checkFunction,
  120. FunctionDeclaration: checkFunction,
  121. FunctionExpression: checkFunction
  122. };
  123. }
  124. };