arrow-parens.js 5.7 KB

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