arrow-parens.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /**
  2. * @fileoverview Rule to require parens in arrow function arguments.
  3. * @author Jxck
  4. * @deprecated in ESLint v8.53.0
  5. */
  6. "use strict";
  7. //------------------------------------------------------------------------------
  8. // Requirements
  9. //------------------------------------------------------------------------------
  10. const astUtils = require("./utils/ast-utils");
  11. //------------------------------------------------------------------------------
  12. // Helpers
  13. //------------------------------------------------------------------------------
  14. /**
  15. * Determines if the given arrow function has block body.
  16. * @param {ASTNode} node `ArrowFunctionExpression` node.
  17. * @returns {boolean} `true` if the function has block body.
  18. */
  19. function hasBlockBody(node) {
  20. return node.body.type === "BlockStatement";
  21. }
  22. //------------------------------------------------------------------------------
  23. // Rule Definition
  24. //------------------------------------------------------------------------------
  25. /** @type {import('../types').Rule.RuleModule} */
  26. module.exports = {
  27. meta: {
  28. deprecated: {
  29. message: "Formatting rules are being moved out of ESLint core.",
  30. url: "https://eslint.org/blog/2023/10/deprecating-formatting-rules/",
  31. deprecatedSince: "8.53.0",
  32. availableUntil: "11.0.0",
  33. replacedBy: [
  34. {
  35. message:
  36. "ESLint Stylistic now maintains deprecated stylistic core rules.",
  37. url: "https://eslint.style/guide/migration",
  38. plugin: {
  39. name: "@stylistic/eslint-plugin",
  40. url: "https://eslint.style",
  41. },
  42. rule: {
  43. name: "arrow-parens",
  44. url: "https://eslint.style/rules/arrow-parens",
  45. },
  46. },
  47. ],
  48. },
  49. type: "layout",
  50. docs: {
  51. description: "Require parentheses around arrow function arguments",
  52. recommended: false,
  53. url: "https://eslint.org/docs/latest/rules/arrow-parens",
  54. },
  55. fixable: "code",
  56. schema: [
  57. {
  58. enum: ["always", "as-needed"],
  59. },
  60. {
  61. type: "object",
  62. properties: {
  63. requireForBlockBody: {
  64. type: "boolean",
  65. default: false,
  66. },
  67. },
  68. additionalProperties: false,
  69. },
  70. ],
  71. messages: {
  72. unexpectedParens:
  73. "Unexpected parentheses around single function argument.",
  74. expectedParens:
  75. "Expected parentheses around arrow function argument.",
  76. unexpectedParensInline:
  77. "Unexpected parentheses around single function argument having a body with no curly braces.",
  78. expectedParensBlock:
  79. "Expected parentheses around arrow function argument having a body with curly braces.",
  80. },
  81. },
  82. create(context) {
  83. const asNeeded = context.options[0] === "as-needed";
  84. const requireForBlockBody =
  85. asNeeded &&
  86. context.options[1] &&
  87. context.options[1].requireForBlockBody === true;
  88. const sourceCode = context.sourceCode;
  89. /**
  90. * Finds opening paren of parameters for the given arrow function, if it exists.
  91. * It is assumed that the given arrow function has exactly one parameter.
  92. * @param {ASTNode} node `ArrowFunctionExpression` node.
  93. * @returns {Token|null} the opening paren, or `null` if the given arrow function doesn't have parens of parameters.
  94. */
  95. function findOpeningParenOfParams(node) {
  96. const tokenBeforeParams = sourceCode.getTokenBefore(node.params[0]);
  97. if (
  98. tokenBeforeParams &&
  99. astUtils.isOpeningParenToken(tokenBeforeParams) &&
  100. node.range[0] <= tokenBeforeParams.range[0]
  101. ) {
  102. return tokenBeforeParams;
  103. }
  104. return null;
  105. }
  106. /**
  107. * Finds closing paren of parameters for the given arrow function.
  108. * It is assumed that the given arrow function has parens of parameters and that it has exactly one parameter.
  109. * @param {ASTNode} node `ArrowFunctionExpression` node.
  110. * @returns {Token} the closing paren of parameters.
  111. */
  112. function getClosingParenOfParams(node) {
  113. return sourceCode.getTokenAfter(
  114. node.params[0],
  115. astUtils.isClosingParenToken,
  116. );
  117. }
  118. /**
  119. * Determines whether the given arrow function has comments inside parens of parameters.
  120. * It is assumed that the given arrow function has parens of parameters.
  121. * @param {ASTNode} node `ArrowFunctionExpression` node.
  122. * @param {Token} openingParen Opening paren of parameters.
  123. * @returns {boolean} `true` if the function has at least one comment inside of parens of parameters.
  124. */
  125. function hasCommentsInParensOfParams(node, openingParen) {
  126. return sourceCode.commentsExistBetween(
  127. openingParen,
  128. getClosingParenOfParams(node),
  129. );
  130. }
  131. /**
  132. * Determines whether the given arrow function has unexpected tokens before opening paren of parameters,
  133. * in which case it will be assumed that the existing parens of parameters are necessary.
  134. * Only tokens within the range of the arrow function (tokens that are part of the arrow function) are taken into account.
  135. * Example: <T>(a) => b
  136. * @param {ASTNode} node `ArrowFunctionExpression` node.
  137. * @param {Token} openingParen Opening paren of parameters.
  138. * @returns {boolean} `true` if the function has at least one unexpected token.
  139. */
  140. function hasUnexpectedTokensBeforeOpeningParen(node, openingParen) {
  141. const expectedCount = node.async ? 1 : 0;
  142. return (
  143. sourceCode.getFirstToken(node, { skip: expectedCount }) !==
  144. openingParen
  145. );
  146. }
  147. return {
  148. "ArrowFunctionExpression[params.length=1]"(node) {
  149. const shouldHaveParens =
  150. !asNeeded || (requireForBlockBody && hasBlockBody(node));
  151. const openingParen = findOpeningParenOfParams(node);
  152. const hasParens = openingParen !== null;
  153. const [param] = node.params;
  154. if (shouldHaveParens && !hasParens) {
  155. context.report({
  156. node,
  157. messageId: requireForBlockBody
  158. ? "expectedParensBlock"
  159. : "expectedParens",
  160. loc: param.loc,
  161. *fix(fixer) {
  162. yield fixer.insertTextBefore(param, "(");
  163. yield fixer.insertTextAfter(param, ")");
  164. },
  165. });
  166. }
  167. if (
  168. !shouldHaveParens &&
  169. hasParens &&
  170. param.type === "Identifier" &&
  171. !param.typeAnnotation &&
  172. !node.returnType &&
  173. !hasCommentsInParensOfParams(node, openingParen) &&
  174. !hasUnexpectedTokensBeforeOpeningParen(node, openingParen)
  175. ) {
  176. context.report({
  177. node,
  178. messageId: requireForBlockBody
  179. ? "unexpectedParensInline"
  180. : "unexpectedParens",
  181. loc: param.loc,
  182. *fix(fixer) {
  183. const tokenBeforeOpeningParen =
  184. sourceCode.getTokenBefore(openingParen);
  185. const closingParen = getClosingParenOfParams(node);
  186. if (
  187. tokenBeforeOpeningParen &&
  188. tokenBeforeOpeningParen.range[1] ===
  189. openingParen.range[0] &&
  190. !astUtils.canTokensBeAdjacent(
  191. tokenBeforeOpeningParen,
  192. sourceCode.getFirstToken(param),
  193. )
  194. ) {
  195. yield fixer.insertTextBefore(openingParen, " ");
  196. }
  197. // remove parens, whitespace inside parens, and possible trailing comma
  198. yield fixer.removeRange([
  199. openingParen.range[0],
  200. param.range[0],
  201. ]);
  202. yield fixer.removeRange([
  203. param.range[1],
  204. closingParen.range[1],
  205. ]);
  206. },
  207. });
  208. }
  209. },
  210. };
  211. },
  212. };