no-arrow-functions.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. /**
  2. * @author Toru Nagashima <https://github.com/mysticatea>
  3. * See LICENSE file in root directory for full license.
  4. */
  5. "use strict"
  6. const { isArrowToken, isParenthesized } = require("eslint-utils")
  7. module.exports = {
  8. meta: {
  9. docs: {
  10. description: "disallow arrow function expressions.",
  11. category: "ES2015",
  12. recommended: false,
  13. url:
  14. "http://mysticatea.github.io/eslint-plugin-es/rules/no-arrow-functions.html",
  15. },
  16. fixable: "code",
  17. messages: {
  18. forbidden: "ES2015 arrow function expressions are forbidden.",
  19. },
  20. schema: [],
  21. type: "problem",
  22. },
  23. create(context) {
  24. const sourceCode = context.getSourceCode()
  25. /**
  26. * ArrowFunctionExpression to FunctionExpression
  27. * @param {Node} node ArrowFunctionExpression Node
  28. * @param {boolean} hasThis `true` if the function has `this`.
  29. * @returns {string} function expression text
  30. */
  31. function toFunctionExpression(node, hasThis) {
  32. const params = node.params
  33. const paramText = params.length
  34. ? sourceCode.text.slice(
  35. params[0].range[0],
  36. params[params.length - 1].range[1]
  37. )
  38. : ""
  39. const arrowToken = sourceCode.getTokenBefore(
  40. node.body,
  41. isArrowToken
  42. )
  43. const preText = sourceCode.text.slice(
  44. arrowToken.range[1],
  45. node.body.range[0]
  46. )
  47. const bodyText = sourceCode.text
  48. .slice(arrowToken.range[1], node.range[1])
  49. .trim()
  50. let resultText =
  51. /*eslint-disable @mysticatea/prettier */
  52. node.body.type === "BlockStatement" ? (
  53. `function(${paramText}) ${bodyText}`
  54. ) : preText.includes("\n") ? (
  55. `function(${paramText}) { return (${bodyText}) }`
  56. ) : (
  57. `function(${paramText}) { return ${bodyText} }`
  58. )
  59. /*eslint-enable @mysticatea/prettier */
  60. if (node.async) {
  61. resultText = `async ${resultText}`
  62. }
  63. if (hasThis) {
  64. resultText += ".bind(this)"
  65. }
  66. if (
  67. node.parent.type === "ExpressionStatement" &&
  68. !isParenthesized(node, sourceCode)
  69. ) {
  70. resultText = `(${resultText})`
  71. }
  72. return resultText
  73. }
  74. /**
  75. * Report that ArrowFunctionExpression is being used
  76. * @param {Node} node ArrowFunctionExpression Node
  77. * @param {boolean} hasThis Whether `this` is referenced in` function` scope
  78. * @param {boolean} hasSuper Whether `super` is referenced in` function` scope
  79. * @returns {void}
  80. */
  81. function report(node, hasThis, hasSuper) {
  82. context.report({
  83. node,
  84. messageId: "forbidden",
  85. fix(fixer) {
  86. if (hasSuper) {
  87. return undefined
  88. }
  89. return fixer.replaceText(
  90. node,
  91. toFunctionExpression(node, hasThis)
  92. )
  93. },
  94. })
  95. }
  96. let stack = { upper: null, hasThis: false, hasSuper: false }
  97. return {
  98. ":function"() {
  99. stack = { upper: stack, hasThis: false, hasSuper: false }
  100. },
  101. ":function:exit"(node) {
  102. const { hasThis, hasSuper } = stack
  103. stack = stack.upper
  104. if (node.type === "ArrowFunctionExpression") {
  105. report(node, hasThis, hasSuper)
  106. stack.hasThis = stack.hasThis || hasThis
  107. stack.hasSuper = stack.hasSuper || hasSuper
  108. }
  109. },
  110. ThisExpression() {
  111. stack.hasThis = true
  112. },
  113. Super() {
  114. stack.hasSuper = true
  115. },
  116. }
  117. },
  118. }