arrow-parens.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /**
  2. * @fileoverview Rule to require parens in arrow function arguments.
  3. * @author Jxck
  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: "require parentheses around arrow function arguments",
  17. category: "ECMAScript 6",
  18. recommended: false,
  19. url: "https://eslint.org/docs/rules/arrow-parens"
  20. },
  21. fixable: "code",
  22. schema: [
  23. {
  24. enum: ["always", "as-needed"]
  25. },
  26. {
  27. type: "object",
  28. properties: {
  29. requireForBlockBody: {
  30. type: "boolean"
  31. }
  32. },
  33. additionalProperties: false
  34. }
  35. ],
  36. messages: {
  37. unexpectedParens: "Unexpected parentheses around single function argument.",
  38. expectedParens: "Expected parentheses around arrow function argument.",
  39. unexpectedParensInline: "Unexpected parentheses around single function argument having a body with no curly braces.",
  40. expectedParensBlock: "Expected parentheses around arrow function argument having a body with curly braces."
  41. }
  42. },
  43. create(context) {
  44. const asNeeded = context.options[0] === "as-needed";
  45. const requireForBlockBody = asNeeded && context.options[1] && context.options[1].requireForBlockBody === true;
  46. const sourceCode = context.getSourceCode();
  47. /**
  48. * Determines whether a arrow function argument end with `)`
  49. * @param {ASTNode} node The arrow function node.
  50. * @returns {void}
  51. */
  52. function parens(node) {
  53. const isAsync = node.async;
  54. const firstTokenOfParam = sourceCode.getFirstToken(node, isAsync ? 1 : 0);
  55. /**
  56. * Remove the parenthesis around a parameter
  57. * @param {Fixer} fixer Fixer
  58. * @returns {string} fixed parameter
  59. */
  60. function fixParamsWithParenthesis(fixer) {
  61. const paramToken = sourceCode.getTokenAfter(firstTokenOfParam);
  62. /*
  63. * ES8 allows Trailing commas in function parameter lists and calls
  64. * https://github.com/eslint/eslint/issues/8834
  65. */
  66. const closingParenToken = sourceCode.getTokenAfter(paramToken, astUtils.isClosingParenToken);
  67. const asyncToken = isAsync ? sourceCode.getTokenBefore(firstTokenOfParam) : null;
  68. const shouldAddSpaceForAsync = asyncToken && (asyncToken.range[1] === firstTokenOfParam.range[0]);
  69. return fixer.replaceTextRange([
  70. firstTokenOfParam.range[0],
  71. closingParenToken.range[1]
  72. ], `${shouldAddSpaceForAsync ? " " : ""}${paramToken.value}`);
  73. }
  74. // "as-needed", { "requireForBlockBody": true }: x => x
  75. if (
  76. requireForBlockBody &&
  77. node.params.length === 1 &&
  78. node.params[0].type === "Identifier" &&
  79. !node.params[0].typeAnnotation &&
  80. node.body.type !== "BlockStatement" &&
  81. !node.returnType
  82. ) {
  83. if (astUtils.isOpeningParenToken(firstTokenOfParam)) {
  84. context.report({
  85. node,
  86. messageId: "unexpectedParensInline",
  87. fix: fixParamsWithParenthesis
  88. });
  89. }
  90. return;
  91. }
  92. if (
  93. requireForBlockBody &&
  94. node.body.type === "BlockStatement"
  95. ) {
  96. if (!astUtils.isOpeningParenToken(firstTokenOfParam)) {
  97. context.report({
  98. node,
  99. messageId: "expectedParensBlock",
  100. fix(fixer) {
  101. return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`);
  102. }
  103. });
  104. }
  105. return;
  106. }
  107. // "as-needed": x => x
  108. if (asNeeded &&
  109. node.params.length === 1 &&
  110. node.params[0].type === "Identifier" &&
  111. !node.params[0].typeAnnotation &&
  112. !node.returnType
  113. ) {
  114. if (astUtils.isOpeningParenToken(firstTokenOfParam)) {
  115. context.report({
  116. node,
  117. messageId: "unexpectedParens",
  118. fix: fixParamsWithParenthesis
  119. });
  120. }
  121. return;
  122. }
  123. if (firstTokenOfParam.type === "Identifier") {
  124. const after = sourceCode.getTokenAfter(firstTokenOfParam);
  125. // (x) => x
  126. if (after.value !== ")") {
  127. context.report({
  128. node,
  129. messageId: "expectedParens",
  130. fix(fixer) {
  131. return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`);
  132. }
  133. });
  134. }
  135. }
  136. }
  137. return {
  138. ArrowFunctionExpression: parens
  139. };
  140. }
  141. };