Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

no-extra-bind.js 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /**
  2. * @fileoverview Rule to flag unnecessary bind calls
  3. * @author Bence Dányi <bence@danyi.me>
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const astUtils = require("../util/ast-utils");
  10. //------------------------------------------------------------------------------
  11. // Helpers
  12. //------------------------------------------------------------------------------
  13. const SIDE_EFFECT_FREE_NODE_TYPES = new Set(["Literal", "Identifier", "ThisExpression", "FunctionExpression"]);
  14. //------------------------------------------------------------------------------
  15. // Rule Definition
  16. //------------------------------------------------------------------------------
  17. module.exports = {
  18. meta: {
  19. type: "suggestion",
  20. docs: {
  21. description: "disallow unnecessary calls to `.bind()`",
  22. category: "Best Practices",
  23. recommended: false,
  24. url: "https://eslint.org/docs/rules/no-extra-bind"
  25. },
  26. schema: [],
  27. fixable: "code",
  28. messages: {
  29. unexpected: "The function binding is unnecessary."
  30. }
  31. },
  32. create(context) {
  33. let scopeInfo = null;
  34. /**
  35. * Checks if a node is free of side effects.
  36. *
  37. * This check is stricter than it needs to be, in order to keep the implementation simple.
  38. *
  39. * @param {ASTNode} node A node to check.
  40. * @returns {boolean} True if the node is known to be side-effect free, false otherwise.
  41. */
  42. function isSideEffectFree(node) {
  43. return SIDE_EFFECT_FREE_NODE_TYPES.has(node.type);
  44. }
  45. /**
  46. * Reports a given function node.
  47. *
  48. * @param {ASTNode} node - A node to report. This is a FunctionExpression or
  49. * an ArrowFunctionExpression.
  50. * @returns {void}
  51. */
  52. function report(node) {
  53. context.report({
  54. node: node.parent.parent,
  55. messageId: "unexpected",
  56. loc: node.parent.property.loc.start,
  57. fix(fixer) {
  58. if (node.parent.parent.arguments.length && !isSideEffectFree(node.parent.parent.arguments[0])) {
  59. return null;
  60. }
  61. const firstTokenToRemove = context.getSourceCode()
  62. .getFirstTokenBetween(node.parent.object, node.parent.property, astUtils.isNotClosingParenToken);
  63. return fixer.removeRange([firstTokenToRemove.range[0], node.parent.parent.range[1]]);
  64. }
  65. });
  66. }
  67. /**
  68. * Checks whether or not a given function node is the callee of `.bind()`
  69. * method.
  70. *
  71. * e.g. `(function() {}.bind(foo))`
  72. *
  73. * @param {ASTNode} node - A node to report. This is a FunctionExpression or
  74. * an ArrowFunctionExpression.
  75. * @returns {boolean} `true` if the node is the callee of `.bind()` method.
  76. */
  77. function isCalleeOfBindMethod(node) {
  78. const parent = node.parent;
  79. const grandparent = parent.parent;
  80. return (
  81. grandparent &&
  82. grandparent.type === "CallExpression" &&
  83. grandparent.callee === parent &&
  84. grandparent.arguments.length === 1 &&
  85. parent.type === "MemberExpression" &&
  86. parent.object === node &&
  87. astUtils.getStaticPropertyName(parent) === "bind"
  88. );
  89. }
  90. /**
  91. * Adds a scope information object to the stack.
  92. *
  93. * @param {ASTNode} node - A node to add. This node is a FunctionExpression
  94. * or a FunctionDeclaration node.
  95. * @returns {void}
  96. */
  97. function enterFunction(node) {
  98. scopeInfo = {
  99. isBound: isCalleeOfBindMethod(node),
  100. thisFound: false,
  101. upper: scopeInfo
  102. };
  103. }
  104. /**
  105. * Removes the scope information object from the top of the stack.
  106. * At the same time, this reports the function node if the function has
  107. * `.bind()` and the `this` keywords found.
  108. *
  109. * @param {ASTNode} node - A node to remove. This node is a
  110. * FunctionExpression or a FunctionDeclaration node.
  111. * @returns {void}
  112. */
  113. function exitFunction(node) {
  114. if (scopeInfo.isBound && !scopeInfo.thisFound) {
  115. report(node);
  116. }
  117. scopeInfo = scopeInfo.upper;
  118. }
  119. /**
  120. * Reports a given arrow function if the function is callee of `.bind()`
  121. * method.
  122. *
  123. * @param {ASTNode} node - A node to report. This node is an
  124. * ArrowFunctionExpression.
  125. * @returns {void}
  126. */
  127. function exitArrowFunction(node) {
  128. if (isCalleeOfBindMethod(node)) {
  129. report(node);
  130. }
  131. }
  132. /**
  133. * Set the mark as the `this` keyword was found in this scope.
  134. *
  135. * @returns {void}
  136. */
  137. function markAsThisFound() {
  138. if (scopeInfo) {
  139. scopeInfo.thisFound = true;
  140. }
  141. }
  142. return {
  143. "ArrowFunctionExpression:exit": exitArrowFunction,
  144. FunctionDeclaration: enterFunction,
  145. "FunctionDeclaration:exit": exitFunction,
  146. FunctionExpression: enterFunction,
  147. "FunctionExpression:exit": exitFunction,
  148. ThisExpression: markAsThisFound
  149. };
  150. }
  151. };