Index: frontend/node_modules/eslint-plugin-react-hooks/LICENSE
===================================================================
--- frontend/node_modules/eslint-plugin-react-hooks/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react-hooks/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) Facebook, Inc. and its affiliates.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
Index: frontend/node_modules/eslint-plugin-react-hooks/README.md
===================================================================
--- frontend/node_modules/eslint-plugin-react-hooks/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react-hooks/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,75 @@
+# `eslint-plugin-react-hooks`
+
+This ESLint plugin enforces the [Rules of Hooks](https://reactjs.org/docs/hooks-rules.html).
+
+It is a part of the [Hooks API](https://reactjs.org/docs/hooks-intro.html) for React.
+
+## Installation
+
+**Note: If you're using Create React App, please use `react-scripts` >= 3 instead of adding it directly.**
+
+Assuming you already have ESLint installed, run:
+
+```sh
+# npm
+npm install eslint-plugin-react-hooks --save-dev
+
+# yarn
+yarn add eslint-plugin-react-hooks --dev
+```
+
+Then extend the recommended eslint config:
+
+```js
+{
+  "extends": [
+    // ...
+    "plugin:react-hooks/recommended"
+  ]
+}
+```
+
+### Custom Configuration
+
+If you want more fine-grained configuration, you can instead add a snippet like this to your ESLint configuration file:
+
+```js
+{
+  "plugins": [
+    // ...
+    "react-hooks"
+  ],
+  "rules": {
+    // ...
+    "react-hooks/rules-of-hooks": "error",
+    "react-hooks/exhaustive-deps": "warn"
+  }
+}
+```
+
+
+## Advanced Configuration
+
+`exhaustive-deps` can be configured to validate dependencies of custom Hooks with the `additionalHooks` option.
+This option accepts a regex to match the names of custom Hooks that have dependencies.
+
+```js
+{
+  "rules": {
+    // ...
+    "react-hooks/exhaustive-deps": ["warn", {
+      "additionalHooks": "(useMyCustomHook|useMyOtherCustomHook)"
+    }]
+  }
+}
+```
+
+We suggest to use this option **very sparingly, if at all**. Generally saying, we recommend most custom Hooks to not use the dependencies argument, and instead provide a higher-level API that is more focused around a specific use case.
+
+## Valid and Invalid Examples
+
+Please refer to the [Rules of Hooks](https://reactjs.org/docs/hooks-rules.html) documentation and the [Hooks FAQ](https://reactjs.org/docs/hooks-faq.html#what-exactly-do-the-lint-rules-enforce) to learn more about this rule.
+
+## License
+
+MIT
Index: frontend/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js
===================================================================
--- frontend/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,2458 @@
+/**
+ * @license React
+ * eslint-plugin-react-hooks.development.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+if (process.env.NODE_ENV !== "production") {
+  (function() {
+'use strict';
+
+function _unsupportedIterableToArray(o, minLen) {
+  if (!o) return;
+  if (typeof o === "string") return _arrayLikeToArray(o, minLen);
+  var n = Object.prototype.toString.call(o).slice(8, -1);
+  if (n === "Object" && o.constructor) n = o.constructor.name;
+  if (n === "Map" || n === "Set") return Array.from(o);
+  if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
+}
+
+function _arrayLikeToArray(arr, len) {
+  if (len == null || len > arr.length) len = arr.length;
+
+  for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
+
+  return arr2;
+}
+
+function _createForOfIteratorHelper(o, allowArrayLike) {
+  var it;
+
+  if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) {
+    if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
+      if (it) o = it;
+      var i = 0;
+
+      var F = function () {};
+
+      return {
+        s: F,
+        n: function () {
+          if (i >= o.length) return {
+            done: true
+          };
+          return {
+            done: false,
+            value: o[i++]
+          };
+        },
+        e: function (e) {
+          throw e;
+        },
+        f: F
+      };
+    }
+
+    throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
+  }
+
+  var normalCompletion = true,
+      didErr = false,
+      err;
+  return {
+    s: function () {
+      it = o[Symbol.iterator]();
+    },
+    n: function () {
+      var step = it.next();
+      normalCompletion = step.done;
+      return step;
+    },
+    e: function (e) {
+      didErr = true;
+      err = e;
+    },
+    f: function () {
+      try {
+        if (!normalCompletion && it.return != null) it.return();
+      } finally {
+        if (didErr) throw err;
+      }
+    }
+  };
+}
+
+/* global BigInt */
+
+function isHookName(s) {
+  return /^use[A-Z0-9].*$/.test(s);
+}
+/**
+ * We consider hooks to be a hook name identifier or a member expression
+ * containing a hook name.
+ */
+
+
+function isHook(node) {
+  if (node.type === 'Identifier') {
+    return isHookName(node.name);
+  } else if (node.type === 'MemberExpression' && !node.computed && isHook(node.property)) {
+    var obj = node.object;
+    var isPascalCaseNameSpace = /^[A-Z].*/;
+    return obj.type === 'Identifier' && isPascalCaseNameSpace.test(obj.name);
+  } else {
+    return false;
+  }
+}
+/**
+ * Checks if the node is a React component name. React component names must
+ * always start with a non-lowercase letter. So `MyComponent` or `_MyComponent`
+ * are valid component names for instance.
+ */
+
+
+function isComponentName(node) {
+  if (node.type === 'Identifier') {
+    return !/^[a-z]/.test(node.name);
+  } else {
+    return false;
+  }
+}
+
+function isReactFunction(node, functionName) {
+  return node.name === functionName || node.type === 'MemberExpression' && node.object.name === 'React' && node.property.name === functionName;
+}
+/**
+ * Checks if the node is a callback argument of forwardRef. This render function
+ * should follow the rules of hooks.
+ */
+
+
+function isForwardRefCallback(node) {
+  return !!(node.parent && node.parent.callee && isReactFunction(node.parent.callee, 'forwardRef'));
+}
+/**
+ * Checks if the node is a callback argument of React.memo. This anonymous
+ * functional component should follow the rules of hooks.
+ */
+
+
+function isMemoCallback(node) {
+  return !!(node.parent && node.parent.callee && isReactFunction(node.parent.callee, 'memo'));
+}
+
+function isInsideComponentOrHook(node) {
+  while (node) {
+    var functionName = getFunctionName(node);
+
+    if (functionName) {
+      if (isComponentName(functionName) || isHook(functionName)) {
+        return true;
+      }
+    }
+
+    if (isForwardRefCallback(node) || isMemoCallback(node)) {
+      return true;
+    }
+
+    node = node.parent;
+  }
+
+  return false;
+}
+
+var RulesOfHooks = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'enforces the Rules of Hooks',
+      recommended: true,
+      url: 'https://reactjs.org/docs/hooks-rules.html'
+    }
+  },
+  create: function (context) {
+    var codePathReactHooksMapStack = [];
+    var codePathSegmentStack = [];
+    return {
+      // Maintain code segment path stack as we traverse.
+      onCodePathSegmentStart: function (segment) {
+        return codePathSegmentStack.push(segment);
+      },
+      onCodePathSegmentEnd: function () {
+        return codePathSegmentStack.pop();
+      },
+      // Maintain code path stack as we traverse.
+      onCodePathStart: function () {
+        return codePathReactHooksMapStack.push(new Map());
+      },
+      // Process our code path.
+      //
+      // Everything is ok if all React Hooks are both reachable from the initial
+      // segment and reachable from every final segment.
+      onCodePathEnd: function (codePath, codePathNode) {
+        var reactHooksMap = codePathReactHooksMapStack.pop();
+
+        if (reactHooksMap.size === 0) {
+          return;
+        } // All of the segments which are cyclic are recorded in this set.
+
+
+        var cyclic = new Set();
+        /**
+         * Count the number of code paths from the start of the function to this
+         * segment. For example:
+         *
+         * ```js
+         * function MyComponent() {
+         *   if (condition) {
+         *     // Segment 1
+         *   } else {
+         *     // Segment 2
+         *   }
+         *   // Segment 3
+         * }
+         * ```
+         *
+         * Segments 1 and 2 have one path to the beginning of `MyComponent` and
+         * segment 3 has two paths to the beginning of `MyComponent` since we
+         * could have either taken the path of segment 1 or segment 2.
+         *
+         * Populates `cyclic` with cyclic segments.
+         */
+
+        function countPathsFromStart(segment, pathHistory) {
+          var cache = countPathsFromStart.cache;
+          var paths = cache.get(segment.id);
+          var pathList = new Set(pathHistory); // If `pathList` includes the current segment then we've found a cycle!
+          // We need to fill `cyclic` with all segments inside cycle
+
+          if (pathList.has(segment.id)) {
+            var pathArray = [].concat(pathList);
+            var cyclicSegments = pathArray.slice(pathArray.indexOf(segment.id) + 1);
+
+            var _iterator = _createForOfIteratorHelper(cyclicSegments),
+                _step;
+
+            try {
+              for (_iterator.s(); !(_step = _iterator.n()).done;) {
+                var cyclicSegment = _step.value;
+                cyclic.add(cyclicSegment);
+              }
+            } catch (err) {
+              _iterator.e(err);
+            } finally {
+              _iterator.f();
+            }
+
+            return BigInt('0');
+          } // add the current segment to pathList
+
+
+          pathList.add(segment.id); // We have a cached `paths`. Return it.
+
+          if (paths !== undefined) {
+            return paths;
+          }
+
+          if (codePath.thrownSegments.includes(segment)) {
+            paths = BigInt('0');
+          } else if (segment.prevSegments.length === 0) {
+            paths = BigInt('1');
+          } else {
+            paths = BigInt('0');
+
+            var _iterator2 = _createForOfIteratorHelper(segment.prevSegments),
+                _step2;
+
+            try {
+              for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
+                var prevSegment = _step2.value;
+                paths += countPathsFromStart(prevSegment, pathList);
+              }
+            } catch (err) {
+              _iterator2.e(err);
+            } finally {
+              _iterator2.f();
+            }
+          } // If our segment is reachable then there should be at least one path
+          // to it from the start of our code path.
+
+
+          if (segment.reachable && paths === BigInt('0')) {
+            cache.delete(segment.id);
+          } else {
+            cache.set(segment.id, paths);
+          }
+
+          return paths;
+        }
+        /**
+         * Count the number of code paths from this segment to the end of the
+         * function. For example:
+         *
+         * ```js
+         * function MyComponent() {
+         *   // Segment 1
+         *   if (condition) {
+         *     // Segment 2
+         *   } else {
+         *     // Segment 3
+         *   }
+         * }
+         * ```
+         *
+         * Segments 2 and 3 have one path to the end of `MyComponent` and
+         * segment 1 has two paths to the end of `MyComponent` since we could
+         * either take the path of segment 1 or segment 2.
+         *
+         * Populates `cyclic` with cyclic segments.
+         */
+
+
+        function countPathsToEnd(segment, pathHistory) {
+          var cache = countPathsToEnd.cache;
+          var paths = cache.get(segment.id);
+          var pathList = new Set(pathHistory); // If `pathList` includes the current segment then we've found a cycle!
+          // We need to fill `cyclic` with all segments inside cycle
+
+          if (pathList.has(segment.id)) {
+            var pathArray = Array.from(pathList);
+            var cyclicSegments = pathArray.slice(pathArray.indexOf(segment.id) + 1);
+
+            var _iterator3 = _createForOfIteratorHelper(cyclicSegments),
+                _step3;
+
+            try {
+              for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
+                var cyclicSegment = _step3.value;
+                cyclic.add(cyclicSegment);
+              }
+            } catch (err) {
+              _iterator3.e(err);
+            } finally {
+              _iterator3.f();
+            }
+
+            return BigInt('0');
+          } // add the current segment to pathList
+
+
+          pathList.add(segment.id); // We have a cached `paths`. Return it.
+
+          if (paths !== undefined) {
+            return paths;
+          }
+
+          if (codePath.thrownSegments.includes(segment)) {
+            paths = BigInt('0');
+          } else if (segment.nextSegments.length === 0) {
+            paths = BigInt('1');
+          } else {
+            paths = BigInt('0');
+
+            var _iterator4 = _createForOfIteratorHelper(segment.nextSegments),
+                _step4;
+
+            try {
+              for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
+                var nextSegment = _step4.value;
+                paths += countPathsToEnd(nextSegment, pathList);
+              }
+            } catch (err) {
+              _iterator4.e(err);
+            } finally {
+              _iterator4.f();
+            }
+          }
+
+          cache.set(segment.id, paths);
+          return paths;
+        }
+        /**
+         * Gets the shortest path length to the start of a code path.
+         * For example:
+         *
+         * ```js
+         * function MyComponent() {
+         *   if (condition) {
+         *     // Segment 1
+         *   }
+         *   // Segment 2
+         * }
+         * ```
+         *
+         * There is only one path from segment 1 to the code path start. Its
+         * length is one so that is the shortest path.
+         *
+         * There are two paths from segment 2 to the code path start. One
+         * through segment 1 with a length of two and another directly to the
+         * start with a length of one. The shortest path has a length of one
+         * so we would return that.
+         */
+
+
+        function shortestPathLengthToStart(segment) {
+          var cache = shortestPathLengthToStart.cache;
+          var length = cache.get(segment.id); // If `length` is null then we found a cycle! Return infinity since
+          // the shortest path is definitely not the one where we looped.
+
+          if (length === null) {
+            return Infinity;
+          } // We have a cached `length`. Return it.
+
+
+          if (length !== undefined) {
+            return length;
+          } // Compute `length` and cache it. Guarding against cycles.
+
+
+          cache.set(segment.id, null);
+
+          if (segment.prevSegments.length === 0) {
+            length = 1;
+          } else {
+            length = Infinity;
+
+            var _iterator5 = _createForOfIteratorHelper(segment.prevSegments),
+                _step5;
+
+            try {
+              for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
+                var prevSegment = _step5.value;
+                var prevLength = shortestPathLengthToStart(prevSegment);
+
+                if (prevLength < length) {
+                  length = prevLength;
+                }
+              }
+            } catch (err) {
+              _iterator5.e(err);
+            } finally {
+              _iterator5.f();
+            }
+
+            length += 1;
+          }
+
+          cache.set(segment.id, length);
+          return length;
+        }
+
+        countPathsFromStart.cache = new Map();
+        countPathsToEnd.cache = new Map();
+        shortestPathLengthToStart.cache = new Map(); // Count all code paths to the end of our component/hook. Also primes
+        // the `countPathsToEnd` cache.
+
+        var allPathsFromStartToEnd = countPathsToEnd(codePath.initialSegment); // Gets the function name for our code path. If the function name is
+        // `undefined` then we know either that we have an anonymous function
+        // expression or our code path is not in a function. In both cases we
+        // will want to error since neither are React function components or
+        // hook functions - unless it is an anonymous function argument to
+        // forwardRef or memo.
+
+        var codePathFunctionName = getFunctionName(codePathNode); // This is a valid code path for React hooks if we are directly in a React
+        // function component or we are in a hook function.
+
+        var isSomewhereInsideComponentOrHook = isInsideComponentOrHook(codePathNode);
+        var isDirectlyInsideComponentOrHook = codePathFunctionName ? isComponentName(codePathFunctionName) || isHook(codePathFunctionName) : isForwardRefCallback(codePathNode) || isMemoCallback(codePathNode); // Compute the earliest finalizer level using information from the
+        // cache. We expect all reachable final segments to have a cache entry
+        // after calling `visitSegment()`.
+
+        var shortestFinalPathLength = Infinity;
+
+        var _iterator6 = _createForOfIteratorHelper(codePath.finalSegments),
+            _step6;
+
+        try {
+          for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
+            var finalSegment = _step6.value;
+
+            if (!finalSegment.reachable) {
+              continue;
+            }
+
+            var length = shortestPathLengthToStart(finalSegment);
+
+            if (length < shortestFinalPathLength) {
+              shortestFinalPathLength = length;
+            }
+          } // Make sure all React Hooks pass our lint invariants. Log warnings
+          // if not.
+
+        } catch (err) {
+          _iterator6.e(err);
+        } finally {
+          _iterator6.f();
+        }
+
+        var _iterator7 = _createForOfIteratorHelper(reactHooksMap),
+            _step7;
+
+        try {
+          for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
+            var _step7$value = _step7.value,
+                segment = _step7$value[0],
+                reactHooks = _step7$value[1];
+
+            // NOTE: We could report here that the hook is not reachable, but
+            // that would be redundant with more general "no unreachable"
+            // lint rules.
+            if (!segment.reachable) {
+              continue;
+            } // If there are any final segments with a shorter path to start then
+            // we possibly have an early return.
+            //
+            // If our segment is a final segment itself then siblings could
+            // possibly be early returns.
+
+
+            var possiblyHasEarlyReturn = segment.nextSegments.length === 0 ? shortestFinalPathLength <= shortestPathLengthToStart(segment) : shortestFinalPathLength < shortestPathLengthToStart(segment); // Count all the paths from the start of our code path to the end of
+            // our code path that go _through_ this segment. The critical piece
+            // of this is _through_. If we just call `countPathsToEnd(segment)`
+            // then we neglect that we may have gone through multiple paths to get
+            // to this point! Consider:
+            //
+            // ```js
+            // function MyComponent() {
+            //   if (a) {
+            //     // Segment 1
+            //   } else {
+            //     // Segment 2
+            //   }
+            //   // Segment 3
+            //   if (b) {
+            //     // Segment 4
+            //   } else {
+            //     // Segment 5
+            //   }
+            // }
+            // ```
+            //
+            // In this component we have four code paths:
+            //
+            // 1. `a = true; b = true`
+            // 2. `a = true; b = false`
+            // 3. `a = false; b = true`
+            // 4. `a = false; b = false`
+            //
+            // From segment 3 there are two code paths to the end through segment
+            // 4 and segment 5. However, we took two paths to get here through
+            // segment 1 and segment 2.
+            //
+            // If we multiply the paths from start (two) by the paths to end (two)
+            // for segment 3 we get four. Which is our desired count.
+
+            var pathsFromStartToEnd = countPathsFromStart(segment) * countPathsToEnd(segment); // Is this hook a part of a cyclic segment?
+
+            var cycled = cyclic.has(segment.id);
+
+            var _iterator8 = _createForOfIteratorHelper(reactHooks),
+                _step8;
+
+            try {
+              for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
+                var hook = _step8.value;
+
+                // Report an error if a hook may be called more then once.
+                if (cycled) {
+                  context.report({
+                    node: hook,
+                    message: "React Hook \"" + context.getSource(hook) + "\" may be executed " + 'more than once. Possibly because it is called in a loop. ' + 'React Hooks must be called in the exact same order in ' + 'every component render.'
+                  });
+                } // If this is not a valid code path for React hooks then we need to
+                // log a warning for every hook in this code path.
+                //
+                // Pick a special message depending on the scope this hook was
+                // called in.
+
+
+                if (isDirectlyInsideComponentOrHook) {
+                  // Report an error if a hook does not reach all finalizing code
+                  // path segments.
+                  //
+                  // Special case when we think there might be an early return.
+                  if (!cycled && pathsFromStartToEnd !== allPathsFromStartToEnd) {
+                    var message = "React Hook \"" + context.getSource(hook) + "\" is called " + 'conditionally. React Hooks must be called in the exact ' + 'same order in every component render.' + (possiblyHasEarlyReturn ? ' Did you accidentally call a React Hook after an' + ' early return?' : '');
+                    context.report({
+                      node: hook,
+                      message: message
+                    });
+                  }
+                } else if (codePathNode.parent && (codePathNode.parent.type === 'MethodDefinition' || codePathNode.parent.type === 'ClassProperty') && codePathNode.parent.value === codePathNode) {
+                  // Custom message for hooks inside a class
+                  var _message = "React Hook \"" + context.getSource(hook) + "\" cannot be called " + 'in a class component. React Hooks must be called in a ' + 'React function component or a custom React Hook function.';
+
+                  context.report({
+                    node: hook,
+                    message: _message
+                  });
+                } else if (codePathFunctionName) {
+                  // Custom message if we found an invalid function name.
+                  var _message2 = "React Hook \"" + context.getSource(hook) + "\" is called in " + ("function \"" + context.getSource(codePathFunctionName) + "\" ") + 'that is neither a React function component nor a custom ' + 'React Hook function.' + ' React component names must start with an uppercase letter.' + ' React Hook names must start with the word "use".';
+
+                  context.report({
+                    node: hook,
+                    message: _message2
+                  });
+                } else if (codePathNode.type === 'Program') {
+                  // These are dangerous if you have inline requires enabled.
+                  var _message3 = "React Hook \"" + context.getSource(hook) + "\" cannot be called " + 'at the top level. React Hooks must be called in a ' + 'React function component or a custom React Hook function.';
+
+                  context.report({
+                    node: hook,
+                    message: _message3
+                  });
+                } else {
+                  // Assume in all other cases the user called a hook in some
+                  // random function callback. This should usually be true for
+                  // anonymous function expressions. Hopefully this is clarifying
+                  // enough in the common case that the incorrect message in
+                  // uncommon cases doesn't matter.
+                  if (isSomewhereInsideComponentOrHook) {
+                    var _message4 = "React Hook \"" + context.getSource(hook) + "\" cannot be called " + 'inside a callback. React Hooks must be called in a ' + 'React function component or a custom React Hook function.';
+
+                    context.report({
+                      node: hook,
+                      message: _message4
+                    });
+                  }
+                }
+              }
+            } catch (err) {
+              _iterator8.e(err);
+            } finally {
+              _iterator8.f();
+            }
+          }
+        } catch (err) {
+          _iterator7.e(err);
+        } finally {
+          _iterator7.f();
+        }
+      },
+      // Missed opportunity...We could visit all `Identifier`s instead of all
+      // `CallExpression`s and check that _every use_ of a hook name is valid.
+      // But that gets complicated and enters type-system territory, so we're
+      // only being strict about hook calls for now.
+      CallExpression: function (node) {
+        if (isHook(node.callee)) {
+          // Add the hook node to a map keyed by the code path segment. We will
+          // do full code path analysis at the end of our code path.
+          var reactHooksMap = last(codePathReactHooksMapStack);
+          var codePathSegment = last(codePathSegmentStack);
+          var reactHooks = reactHooksMap.get(codePathSegment);
+
+          if (!reactHooks) {
+            reactHooks = [];
+            reactHooksMap.set(codePathSegment, reactHooks);
+          }
+
+          reactHooks.push(node.callee);
+        }
+      }
+    };
+  }
+};
+/**
+ * Gets the static name of a function AST node. For function declarations it is
+ * easy. For anonymous function expressions it is much harder. If you search for
+ * `IsAnonymousFunctionDefinition()` in the ECMAScript spec you'll find places
+ * where JS gives anonymous function expressions names. We roughly detect the
+ * same AST nodes with some exceptions to better fit our use case.
+ */
+
+function getFunctionName(node) {
+  if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' && node.id) {
+    // function useHook() {}
+    // const whatever = function useHook() {};
+    //
+    // Function declaration or function expression names win over any
+    // assignment statements or other renames.
+    return node.id;
+  } else if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
+    if (node.parent.type === 'VariableDeclarator' && node.parent.init === node) {
+      // const useHook = () => {};
+      return node.parent.id;
+    } else if (node.parent.type === 'AssignmentExpression' && node.parent.right === node && node.parent.operator === '=') {
+      // useHook = () => {};
+      return node.parent.left;
+    } else if (node.parent.type === 'Property' && node.parent.value === node && !node.parent.computed) {
+      // {useHook: () => {}}
+      // {useHook() {}}
+      return node.parent.key; // NOTE: We could also support `ClassProperty` and `MethodDefinition`
+      // here to be pedantic. However, hooks in a class are an anti-pattern. So
+      // we don't allow it to error early.
+      //
+      // class {useHook = () => {}}
+      // class {useHook() {}}
+    } else if (node.parent.type === 'AssignmentPattern' && node.parent.right === node && !node.parent.computed) {
+      // const {useHook = () => {}} = {};
+      // ({useHook = () => {}} = {});
+      //
+      // Kinda clowny, but we'd said we'd follow spec convention for
+      // `IsAnonymousFunctionDefinition()` usage.
+      return node.parent.left;
+    } else {
+      return undefined;
+    }
+  } else {
+    return undefined;
+  }
+}
+/**
+ * Convenience function for peeking the last item in a stack.
+ */
+
+
+function last(array) {
+  return array[array.length - 1];
+}
+
+/* eslint-disable no-for-of-loops/no-for-of-loops */
+var ExhaustiveDeps = {
+  meta: {
+    type: 'suggestion',
+    docs: {
+      description: 'verifies the list of dependencies for Hooks like useEffect and similar',
+      recommended: true,
+      url: 'https://github.com/facebook/react/issues/14920'
+    },
+    fixable: 'code',
+    hasSuggestions: true,
+    schema: [{
+      type: 'object',
+      additionalProperties: false,
+      enableDangerousAutofixThisMayCauseInfiniteLoops: false,
+      properties: {
+        additionalHooks: {
+          type: 'string'
+        },
+        enableDangerousAutofixThisMayCauseInfiniteLoops: {
+          type: 'boolean'
+        }
+      }
+    }]
+  },
+  create: function (context) {
+    // Parse the `additionalHooks` regex.
+    var additionalHooks = context.options && context.options[0] && context.options[0].additionalHooks ? new RegExp(context.options[0].additionalHooks) : undefined;
+    var enableDangerousAutofixThisMayCauseInfiniteLoops = context.options && context.options[0] && context.options[0].enableDangerousAutofixThisMayCauseInfiniteLoops || false;
+    var options = {
+      additionalHooks: additionalHooks,
+      enableDangerousAutofixThisMayCauseInfiniteLoops: enableDangerousAutofixThisMayCauseInfiniteLoops
+    };
+
+    function reportProblem(problem) {
+      if (enableDangerousAutofixThisMayCauseInfiniteLoops) {
+        // Used to enable legacy behavior. Dangerous.
+        // Keep this as an option until major IDEs upgrade (including VSCode FB ESLint extension).
+        if (Array.isArray(problem.suggest) && problem.suggest.length > 0) {
+          problem.fix = problem.suggest[0].fix;
+        }
+      }
+
+      context.report(problem);
+    }
+
+    var scopeManager = context.getSourceCode().scopeManager; // Should be shared between visitors.
+
+    var setStateCallSites = new WeakMap();
+    var stateVariables = new WeakSet();
+    var stableKnownValueCache = new WeakMap();
+    var functionWithoutCapturedValueCache = new WeakMap();
+
+    function memoizeWithWeakMap(fn, map) {
+      return function (arg) {
+        if (map.has(arg)) {
+          // to verify cache hits:
+          // console.log(arg.name)
+          return map.get(arg);
+        }
+
+        var result = fn(arg);
+        map.set(arg, result);
+        return result;
+      };
+    }
+    /**
+     * Visitor for both function expressions and arrow function expressions.
+     */
+
+
+    function visitFunctionWithDependencies(node, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect) {
+      if (isEffect && node.async) {
+        reportProblem({
+          node: node,
+          message: "Effect callbacks are synchronous to prevent race conditions. " + "Put the async function inside:\n\n" + 'useEffect(() => {\n' + '  async function fetchData() {\n' + '    // You can await here\n' + '    const response = await MyAPI.getData(someId);\n' + '    // ...\n' + '  }\n' + '  fetchData();\n' + "}, [someId]); // Or [] if effect doesn't need props or state\n\n" + 'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching'
+        });
+      } // Get the current scope.
+
+
+      var scope = scopeManager.acquire(node); // Find all our "pure scopes". On every re-render of a component these
+      // pure scopes may have changes to the variables declared within. So all
+      // variables used in our reactive hook callback but declared in a pure
+      // scope need to be listed as dependencies of our reactive hook callback.
+      //
+      // According to the rules of React you can't read a mutable value in pure
+      // scope. We can't enforce this in a lint so we trust that all variables
+      // declared outside of pure scope are indeed frozen.
+
+      var pureScopes = new Set();
+      var componentScope = null;
+      {
+        var currentScope = scope.upper;
+
+        while (currentScope) {
+          pureScopes.add(currentScope);
+
+          if (currentScope.type === 'function') {
+            break;
+          }
+
+          currentScope = currentScope.upper;
+        } // If there is no parent function scope then there are no pure scopes.
+        // The ones we've collected so far are incorrect. So don't continue with
+        // the lint.
+
+
+        if (!currentScope) {
+          return;
+        }
+
+        componentScope = currentScope;
+      }
+      var isArray = Array.isArray; // Next we'll define a few helpers that helps us
+      // tell if some values don't have to be declared as deps.
+      // Some are known to be stable based on Hook calls.
+      // const [state, setState] = useState() / React.useState()
+      //               ^^^ true for this reference
+      // const [state, dispatch] = useReducer() / React.useReducer()
+      //               ^^^ true for this reference
+      // const ref = useRef()
+      //       ^^^ true for this reference
+      // False for everything else.
+
+      function isStableKnownHookValue(resolved) {
+        if (!isArray(resolved.defs)) {
+          return false;
+        }
+
+        var def = resolved.defs[0];
+
+        if (def == null) {
+          return false;
+        } // Look for `let stuff = ...`
+
+
+        if (def.node.type !== 'VariableDeclarator') {
+          return false;
+        }
+
+        var init = def.node.init;
+
+        if (init == null) {
+          return false;
+        }
+
+        while (init.type === 'TSAsExpression') {
+          init = init.expression;
+        } // Detect primitive constants
+        // const foo = 42
+
+
+        var declaration = def.node.parent;
+
+        if (declaration == null) {
+          // This might happen if variable is declared after the callback.
+          // In that case ESLint won't set up .parent refs.
+          // So we'll set them up manually.
+          fastFindReferenceWithParent(componentScope.block, def.node.id);
+          declaration = def.node.parent;
+
+          if (declaration == null) {
+            return false;
+          }
+        }
+
+        if (declaration.kind === 'const' && init.type === 'Literal' && (typeof init.value === 'string' || typeof init.value === 'number' || init.value === null)) {
+          // Definitely stable
+          return true;
+        } // Detect known Hook calls
+        // const [_, setState] = useState()
+
+
+        if (init.type !== 'CallExpression') {
+          return false;
+        }
+
+        var callee = init.callee; // Step into `= React.something` initializer.
+
+        if (callee.type === 'MemberExpression' && callee.object.name === 'React' && callee.property != null && !callee.computed) {
+          callee = callee.property;
+        }
+
+        if (callee.type !== 'Identifier') {
+          return false;
+        }
+
+        var id = def.node.id;
+        var _callee = callee,
+            name = _callee.name;
+
+        if (name === 'useRef' && id.type === 'Identifier') {
+          // useRef() return value is stable.
+          return true;
+        } else if (name === 'useState' || name === 'useReducer') {
+          // Only consider second value in initializing tuple stable.
+          if (id.type === 'ArrayPattern' && id.elements.length === 2 && isArray(resolved.identifiers)) {
+            // Is second tuple value the same reference we're checking?
+            if (id.elements[1] === resolved.identifiers[0]) {
+              if (name === 'useState') {
+                var references = resolved.references;
+                var writeCount = 0;
+
+                for (var i = 0; i < references.length; i++) {
+                  if (references[i].isWrite()) {
+                    writeCount++;
+                  }
+
+                  if (writeCount > 1) {
+                    return false;
+                  }
+
+                  setStateCallSites.set(references[i].identifier, id.elements[0]);
+                }
+              } // Setter is stable.
+
+
+              return true;
+            } else if (id.elements[0] === resolved.identifiers[0]) {
+              if (name === 'useState') {
+                var _references = resolved.references;
+
+                for (var _i = 0; _i < _references.length; _i++) {
+                  stateVariables.add(_references[_i].identifier);
+                }
+              } // State variable itself is dynamic.
+
+
+              return false;
+            }
+          }
+        } else if (name === 'useTransition') {
+          // Only consider second value in initializing tuple stable.
+          if (id.type === 'ArrayPattern' && id.elements.length === 2 && Array.isArray(resolved.identifiers)) {
+            // Is second tuple value the same reference we're checking?
+            if (id.elements[1] === resolved.identifiers[0]) {
+              // Setter is stable.
+              return true;
+            }
+          }
+        } // By default assume it's dynamic.
+
+
+        return false;
+      } // Some are just functions that don't reference anything dynamic.
+
+
+      function isFunctionWithoutCapturedValues(resolved) {
+        if (!isArray(resolved.defs)) {
+          return false;
+        }
+
+        var def = resolved.defs[0];
+
+        if (def == null) {
+          return false;
+        }
+
+        if (def.node == null || def.node.id == null) {
+          return false;
+        } // Search the direct component subscopes for
+        // top-level function definitions matching this reference.
+
+
+        var fnNode = def.node;
+        var childScopes = componentScope.childScopes;
+        var fnScope = null;
+        var i;
+
+        for (i = 0; i < childScopes.length; i++) {
+          var childScope = childScopes[i];
+          var childScopeBlock = childScope.block;
+
+          if ( // function handleChange() {}
+          fnNode.type === 'FunctionDeclaration' && childScopeBlock === fnNode || // const handleChange = () => {}
+          // const handleChange = function() {}
+          fnNode.type === 'VariableDeclarator' && childScopeBlock.parent === fnNode) {
+            // Found it!
+            fnScope = childScope;
+            break;
+          }
+        }
+
+        if (fnScope == null) {
+          return false;
+        } // Does this function capture any values
+        // that are in pure scopes (aka render)?
+
+
+        for (i = 0; i < fnScope.through.length; i++) {
+          var ref = fnScope.through[i];
+
+          if (ref.resolved == null) {
+            continue;
+          }
+
+          if (pureScopes.has(ref.resolved.scope) && // Stable values are fine though,
+          // although we won't check functions deeper.
+          !memoizedIsStableKnownHookValue(ref.resolved)) {
+            return false;
+          }
+        } // If we got here, this function doesn't capture anything
+        // from render--or everything it captures is known stable.
+
+
+        return true;
+      } // Remember such values. Avoid re-running extra checks on them.
+
+
+      var memoizedIsStableKnownHookValue = memoizeWithWeakMap(isStableKnownHookValue, stableKnownValueCache);
+      var memoizedIsFunctionWithoutCapturedValues = memoizeWithWeakMap(isFunctionWithoutCapturedValues, functionWithoutCapturedValueCache); // These are usually mistaken. Collect them.
+
+      var currentRefsInEffectCleanup = new Map(); // Is this reference inside a cleanup function for this effect node?
+      // We can check by traversing scopes upwards  from the reference, and checking
+      // if the last "return () => " we encounter is located directly inside the effect.
+
+      function isInsideEffectCleanup(reference) {
+        var curScope = reference.from;
+        var isInReturnedFunction = false;
+
+        while (curScope.block !== node) {
+          if (curScope.type === 'function') {
+            isInReturnedFunction = curScope.block.parent != null && curScope.block.parent.type === 'ReturnStatement';
+          }
+
+          curScope = curScope.upper;
+        }
+
+        return isInReturnedFunction;
+      } // Get dependencies from all our resolved references in pure scopes.
+      // Key is dependency string, value is whether it's stable.
+
+
+      var dependencies = new Map();
+      var optionalChains = new Map();
+      gatherDependenciesRecursively(scope);
+
+      function gatherDependenciesRecursively(currentScope) {
+        var _iterator = _createForOfIteratorHelper(currentScope.references),
+            _step;
+
+        try {
+          for (_iterator.s(); !(_step = _iterator.n()).done;) {
+            var reference = _step.value;
+
+            // If this reference is not resolved or it is not declared in a pure
+            // scope then we don't care about this reference.
+            if (!reference.resolved) {
+              continue;
+            }
+
+            if (!pureScopes.has(reference.resolved.scope)) {
+              continue;
+            } // Narrow the scope of a dependency if it is, say, a member expression.
+            // Then normalize the narrowed dependency.
+
+
+            var referenceNode = fastFindReferenceWithParent(node, reference.identifier);
+            var dependencyNode = getDependency(referenceNode);
+            var dependency = analyzePropertyChain(dependencyNode, optionalChains); // Accessing ref.current inside effect cleanup is bad.
+
+            if ( // We're in an effect...
+            isEffect && // ... and this look like accessing .current...
+            dependencyNode.type === 'Identifier' && (dependencyNode.parent.type === 'MemberExpression' || dependencyNode.parent.type === 'OptionalMemberExpression') && !dependencyNode.parent.computed && dependencyNode.parent.property.type === 'Identifier' && dependencyNode.parent.property.name === 'current' && // ...in a cleanup function or below...
+            isInsideEffectCleanup(reference)) {
+              currentRefsInEffectCleanup.set(dependency, {
+                reference: reference,
+                dependencyNode: dependencyNode
+              });
+            }
+
+            if (dependencyNode.parent.type === 'TSTypeQuery' || dependencyNode.parent.type === 'TSTypeReference') {
+              continue;
+            }
+
+            var def = reference.resolved.defs[0];
+
+            if (def == null) {
+              continue;
+            } // Ignore references to the function itself as it's not defined yet.
+
+
+            if (def.node != null && def.node.init === node.parent) {
+              continue;
+            } // Ignore Flow type parameters
+
+
+            if (def.type === 'TypeParameter') {
+              continue;
+            } // Add the dependency to a map so we can make sure it is referenced
+            // again in our dependencies array. Remember whether it's stable.
+
+
+            if (!dependencies.has(dependency)) {
+              var resolved = reference.resolved;
+              var isStable = memoizedIsStableKnownHookValue(resolved) || memoizedIsFunctionWithoutCapturedValues(resolved);
+              dependencies.set(dependency, {
+                isStable: isStable,
+                references: [reference]
+              });
+            } else {
+              dependencies.get(dependency).references.push(reference);
+            }
+          }
+        } catch (err) {
+          _iterator.e(err);
+        } finally {
+          _iterator.f();
+        }
+
+        var _iterator2 = _createForOfIteratorHelper(currentScope.childScopes),
+            _step2;
+
+        try {
+          for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
+            var childScope = _step2.value;
+            gatherDependenciesRecursively(childScope);
+          }
+        } catch (err) {
+          _iterator2.e(err);
+        } finally {
+          _iterator2.f();
+        }
+      } // Warn about accessing .current in cleanup effects.
+
+
+      currentRefsInEffectCleanup.forEach(function (_ref, dependency) {
+        var reference = _ref.reference,
+            dependencyNode = _ref.dependencyNode;
+        var references = reference.resolved.references; // Is React managing this ref or us?
+        // Let's see if we can find a .current assignment.
+
+        var foundCurrentAssignment = false;
+
+        for (var i = 0; i < references.length; i++) {
+          var identifier = references[i].identifier;
+          var parent = identifier.parent;
+
+          if (parent != null && // ref.current
+          // Note: no need to handle OptionalMemberExpression because it can't be LHS.
+          parent.type === 'MemberExpression' && !parent.computed && parent.property.type === 'Identifier' && parent.property.name === 'current' && // ref.current = <something>
+          parent.parent.type === 'AssignmentExpression' && parent.parent.left === parent) {
+            foundCurrentAssignment = true;
+            break;
+          }
+        } // We only want to warn about React-managed refs.
+
+
+        if (foundCurrentAssignment) {
+          return;
+        }
+
+        reportProblem({
+          node: dependencyNode.parent.property,
+          message: "The ref value '" + dependency + ".current' will likely have " + "changed by the time this effect cleanup function runs. If " + "this ref points to a node rendered by React, copy " + ("'" + dependency + ".current' to a variable inside the effect, and ") + "use that variable in the cleanup function."
+        });
+      }); // Warn about assigning to variables in the outer scope.
+      // Those are usually bugs.
+
+      var staleAssignments = new Set();
+
+      function reportStaleAssignment(writeExpr, key) {
+        if (staleAssignments.has(key)) {
+          return;
+        }
+
+        staleAssignments.add(key);
+        reportProblem({
+          node: writeExpr,
+          message: "Assignments to the '" + key + "' variable from inside React Hook " + (context.getSource(reactiveHook) + " will be lost after each ") + "render. To preserve the value over time, store it in a useRef " + "Hook and keep the mutable value in the '.current' property. " + "Otherwise, you can move this variable directly inside " + (context.getSource(reactiveHook) + ".")
+        });
+      } // Remember which deps are stable and report bad usage first.
+
+
+      var stableDependencies = new Set();
+      dependencies.forEach(function (_ref2, key) {
+        var isStable = _ref2.isStable,
+            references = _ref2.references;
+
+        if (isStable) {
+          stableDependencies.add(key);
+        }
+
+        references.forEach(function (reference) {
+          if (reference.writeExpr) {
+            reportStaleAssignment(reference.writeExpr, key);
+          }
+        });
+      });
+
+      if (staleAssignments.size > 0) {
+        // The intent isn't clear so we'll wait until you fix those first.
+        return;
+      }
+
+      if (!declaredDependenciesNode) {
+        // Check if there are any top-level setState() calls.
+        // Those tend to lead to infinite loops.
+        var setStateInsideEffectWithoutDeps = null;
+        dependencies.forEach(function (_ref3, key) {
+          var isStable = _ref3.isStable,
+              references = _ref3.references;
+
+          if (setStateInsideEffectWithoutDeps) {
+            return;
+          }
+
+          references.forEach(function (reference) {
+            if (setStateInsideEffectWithoutDeps) {
+              return;
+            }
+
+            var id = reference.identifier;
+            var isSetState = setStateCallSites.has(id);
+
+            if (!isSetState) {
+              return;
+            }
+
+            var fnScope = reference.from;
+
+            while (fnScope.type !== 'function') {
+              fnScope = fnScope.upper;
+            }
+
+            var isDirectlyInsideEffect = fnScope.block === node;
+
+            if (isDirectlyInsideEffect) {
+              // TODO: we could potentially ignore early returns.
+              setStateInsideEffectWithoutDeps = key;
+            }
+          });
+        });
+
+        if (setStateInsideEffectWithoutDeps) {
+          var _collectRecommendatio = collectRecommendations({
+            dependencies: dependencies,
+            declaredDependencies: [],
+            stableDependencies: stableDependencies,
+            externalDependencies: new Set(),
+            isEffect: true
+          }),
+              _suggestedDependencies = _collectRecommendatio.suggestedDependencies;
+
+          reportProblem({
+            node: reactiveHook,
+            message: "React Hook " + reactiveHookName + " contains a call to '" + setStateInsideEffectWithoutDeps + "'. " + "Without a list of dependencies, this can lead to an infinite chain of updates. " + "To fix this, pass [" + _suggestedDependencies.join(', ') + ("] as a second argument to the " + reactiveHookName + " Hook."),
+            suggest: [{
+              desc: "Add dependencies array: [" + _suggestedDependencies.join(', ') + "]",
+              fix: function (fixer) {
+                return fixer.insertTextAfter(node, ", [" + _suggestedDependencies.join(', ') + "]");
+              }
+            }]
+          });
+        }
+
+        return;
+      }
+
+      var declaredDependencies = [];
+      var externalDependencies = new Set();
+
+      if (declaredDependenciesNode.type !== 'ArrayExpression') {
+        // If the declared dependencies are not an array expression then we
+        // can't verify that the user provided the correct dependencies. Tell
+        // the user this in an error.
+        reportProblem({
+          node: declaredDependenciesNode,
+          message: "React Hook " + context.getSource(reactiveHook) + " was passed a " + 'dependency list that is not an array literal. This means we ' + "can't statically verify whether you've passed the correct " + 'dependencies.'
+        });
+      } else {
+        declaredDependenciesNode.elements.forEach(function (declaredDependencyNode) {
+          // Skip elided elements.
+          if (declaredDependencyNode === null) {
+            return;
+          } // If we see a spread element then add a special warning.
+
+
+          if (declaredDependencyNode.type === 'SpreadElement') {
+            reportProblem({
+              node: declaredDependencyNode,
+              message: "React Hook " + context.getSource(reactiveHook) + " has a spread " + "element in its dependency array. This means we can't " + "statically verify whether you've passed the " + 'correct dependencies.'
+            });
+            return;
+          } // Try to normalize the declared dependency. If we can't then an error
+          // will be thrown. We will catch that error and report an error.
+
+
+          var declaredDependency;
+
+          try {
+            declaredDependency = analyzePropertyChain(declaredDependencyNode, null);
+          } catch (error) {
+            if (/Unsupported node type/.test(error.message)) {
+              if (declaredDependencyNode.type === 'Literal') {
+                if (dependencies.has(declaredDependencyNode.value)) {
+                  reportProblem({
+                    node: declaredDependencyNode,
+                    message: "The " + declaredDependencyNode.raw + " literal is not a valid dependency " + "because it never changes. " + ("Did you mean to include " + declaredDependencyNode.value + " in the array instead?")
+                  });
+                } else {
+                  reportProblem({
+                    node: declaredDependencyNode,
+                    message: "The " + declaredDependencyNode.raw + " literal is not a valid dependency " + 'because it never changes. You can safely remove it.'
+                  });
+                }
+              } else {
+                reportProblem({
+                  node: declaredDependencyNode,
+                  message: "React Hook " + context.getSource(reactiveHook) + " has a " + "complex expression in the dependency array. " + 'Extract it to a separate variable so it can be statically checked.'
+                });
+              }
+
+              return;
+            } else {
+              throw error;
+            }
+          }
+
+          var maybeID = declaredDependencyNode;
+
+          while (maybeID.type === 'MemberExpression' || maybeID.type === 'OptionalMemberExpression' || maybeID.type === 'ChainExpression') {
+            maybeID = maybeID.object || maybeID.expression.object;
+          }
+
+          var isDeclaredInComponent = !componentScope.through.some(function (ref) {
+            return ref.identifier === maybeID;
+          }); // Add the dependency to our declared dependency map.
+
+          declaredDependencies.push({
+            key: declaredDependency,
+            node: declaredDependencyNode
+          });
+
+          if (!isDeclaredInComponent) {
+            externalDependencies.add(declaredDependency);
+          }
+        });
+      }
+
+      var _collectRecommendatio2 = collectRecommendations({
+        dependencies: dependencies,
+        declaredDependencies: declaredDependencies,
+        stableDependencies: stableDependencies,
+        externalDependencies: externalDependencies,
+        isEffect: isEffect
+      }),
+          suggestedDependencies = _collectRecommendatio2.suggestedDependencies,
+          unnecessaryDependencies = _collectRecommendatio2.unnecessaryDependencies,
+          missingDependencies = _collectRecommendatio2.missingDependencies,
+          duplicateDependencies = _collectRecommendatio2.duplicateDependencies;
+
+      var suggestedDeps = suggestedDependencies;
+      var problemCount = duplicateDependencies.size + missingDependencies.size + unnecessaryDependencies.size;
+
+      if (problemCount === 0) {
+        // If nothing else to report, check if some dependencies would
+        // invalidate on every render.
+        var constructions = scanForConstructions({
+          declaredDependencies: declaredDependencies,
+          declaredDependenciesNode: declaredDependenciesNode,
+          componentScope: componentScope,
+          scope: scope
+        });
+        constructions.forEach(function (_ref4) {
+          var construction = _ref4.construction,
+              isUsedOutsideOfHook = _ref4.isUsedOutsideOfHook,
+              depType = _ref4.depType;
+          var wrapperHook = depType === 'function' ? 'useCallback' : 'useMemo';
+          var constructionType = depType === 'function' ? 'definition' : 'initialization';
+          var defaultAdvice = "wrap the " + constructionType + " of '" + construction.name.name + "' in its own " + wrapperHook + "() Hook.";
+          var advice = isUsedOutsideOfHook ? "To fix this, " + defaultAdvice : "Move it inside the " + reactiveHookName + " callback. Alternatively, " + defaultAdvice;
+          var causation = depType === 'conditional' || depType === 'logical expression' ? 'could make' : 'makes';
+          var message = "The '" + construction.name.name + "' " + depType + " " + causation + " the dependencies of " + (reactiveHookName + " Hook (at line " + declaredDependenciesNode.loc.start.line + ") ") + ("change on every render. " + advice);
+          var suggest; // Only handle the simple case of variable assignments.
+          // Wrapping function declarations can mess up hoisting.
+
+          if (isUsedOutsideOfHook && construction.type === 'Variable' && // Objects may be mutated after construction, which would make this
+          // fix unsafe. Functions _probably_ won't be mutated, so we'll
+          // allow this fix for them.
+          depType === 'function') {
+            suggest = [{
+              desc: "Wrap the " + constructionType + " of '" + construction.name.name + "' in its own " + wrapperHook + "() Hook.",
+              fix: function (fixer) {
+                var _ref5 = wrapperHook === 'useMemo' ? ["useMemo(() => { return ", '; })'] : ['useCallback(', ')'],
+                    before = _ref5[0],
+                    after = _ref5[1];
+
+                return [// TODO: also add an import?
+                fixer.insertTextBefore(construction.node.init, before), // TODO: ideally we'd gather deps here but it would require
+                // restructuring the rule code. This will cause a new lint
+                // error to appear immediately for useCallback. Note we're
+                // not adding [] because would that changes semantics.
+                fixer.insertTextAfter(construction.node.init, after)];
+              }
+            }];
+          } // TODO: What if the function needs to change on every render anyway?
+          // Should we suggest removing effect deps as an appropriate fix too?
+
+
+          reportProblem({
+            // TODO: Why not report this at the dependency site?
+            node: construction.node,
+            message: message,
+            suggest: suggest
+          });
+        });
+        return;
+      } // If we're going to report a missing dependency,
+      // we might as well recalculate the list ignoring
+      // the currently specified deps. This can result
+      // in some extra deduplication. We can't do this
+      // for effects though because those have legit
+      // use cases for over-specifying deps.
+
+
+      if (!isEffect && missingDependencies.size > 0) {
+        suggestedDeps = collectRecommendations({
+          dependencies: dependencies,
+          declaredDependencies: [],
+          // Pretend we don't know
+          stableDependencies: stableDependencies,
+          externalDependencies: externalDependencies,
+          isEffect: isEffect
+        }).suggestedDependencies;
+      } // Alphabetize the suggestions, but only if deps were already alphabetized.
+
+
+      function areDeclaredDepsAlphabetized() {
+        if (declaredDependencies.length === 0) {
+          return true;
+        }
+
+        var declaredDepKeys = declaredDependencies.map(function (dep) {
+          return dep.key;
+        });
+        var sortedDeclaredDepKeys = declaredDepKeys.slice().sort();
+        return declaredDepKeys.join(',') === sortedDeclaredDepKeys.join(',');
+      }
+
+      if (areDeclaredDepsAlphabetized()) {
+        suggestedDeps.sort();
+      } // Most of our algorithm deals with dependency paths with optional chaining stripped.
+      // This function is the last step before printing a dependency, so now is a good time to
+      // check whether any members in our path are always used as optional-only. In that case,
+      // we will use ?. instead of . to concatenate those parts of the path.
+
+
+      function formatDependency(path) {
+        var members = path.split('.');
+        var finalPath = '';
+
+        for (var i = 0; i < members.length; i++) {
+          if (i !== 0) {
+            var pathSoFar = members.slice(0, i + 1).join('.');
+            var isOptional = optionalChains.get(pathSoFar) === true;
+            finalPath += isOptional ? '?.' : '.';
+          }
+
+          finalPath += members[i];
+        }
+
+        return finalPath;
+      }
+
+      function getWarningMessage(deps, singlePrefix, label, fixVerb) {
+        if (deps.size === 0) {
+          return null;
+        }
+
+        return (deps.size > 1 ? '' : singlePrefix + ' ') + label + ' ' + (deps.size > 1 ? 'dependencies' : 'dependency') + ': ' + joinEnglish(Array.from(deps).sort().map(function (name) {
+          return "'" + formatDependency(name) + "'";
+        })) + (". Either " + fixVerb + " " + (deps.size > 1 ? 'them' : 'it') + " or remove the dependency array.");
+      }
+
+      var extraWarning = '';
+
+      if (unnecessaryDependencies.size > 0) {
+        var badRef = null;
+        Array.from(unnecessaryDependencies.keys()).forEach(function (key) {
+          if (badRef !== null) {
+            return;
+          }
+
+          if (key.endsWith('.current')) {
+            badRef = key;
+          }
+        });
+
+        if (badRef !== null) {
+          extraWarning = " Mutable values like '" + badRef + "' aren't valid dependencies " + "because mutating them doesn't re-render the component.";
+        } else if (externalDependencies.size > 0) {
+          var dep = Array.from(externalDependencies)[0]; // Don't show this warning for things that likely just got moved *inside* the callback
+          // because in that case they're clearly not referring to globals.
+
+          if (!scope.set.has(dep)) {
+            extraWarning = " Outer scope values like '" + dep + "' aren't valid dependencies " + "because mutating them doesn't re-render the component.";
+          }
+        }
+      } // `props.foo()` marks `props` as a dependency because it has
+      // a `this` value. This warning can be confusing.
+      // So if we're going to show it, append a clarification.
+
+
+      if (!extraWarning && missingDependencies.has('props')) {
+        var propDep = dependencies.get('props');
+
+        if (propDep == null) {
+          return;
+        }
+
+        var refs = propDep.references;
+
+        if (!Array.isArray(refs)) {
+          return;
+        }
+
+        var isPropsOnlyUsedInMembers = true;
+
+        for (var i = 0; i < refs.length; i++) {
+          var ref = refs[i];
+          var id = fastFindReferenceWithParent(componentScope.block, ref.identifier);
+
+          if (!id) {
+            isPropsOnlyUsedInMembers = false;
+            break;
+          }
+
+          var parent = id.parent;
+
+          if (parent == null) {
+            isPropsOnlyUsedInMembers = false;
+            break;
+          }
+
+          if (parent.type !== 'MemberExpression' && parent.type !== 'OptionalMemberExpression') {
+            isPropsOnlyUsedInMembers = false;
+            break;
+          }
+        }
+
+        if (isPropsOnlyUsedInMembers) {
+          extraWarning = " However, 'props' will change when *any* prop changes, so the " + "preferred fix is to destructure the 'props' object outside of " + ("the " + reactiveHookName + " call and refer to those specific props ") + ("inside " + context.getSource(reactiveHook) + ".");
+        }
+      }
+
+      if (!extraWarning && missingDependencies.size > 0) {
+        // See if the user is trying to avoid specifying a callable prop.
+        // This usually means they're unaware of useCallback.
+        var missingCallbackDep = null;
+        missingDependencies.forEach(function (missingDep) {
+          if (missingCallbackDep) {
+            return;
+          } // Is this a variable from top scope?
+
+
+          var topScopeRef = componentScope.set.get(missingDep);
+          var usedDep = dependencies.get(missingDep);
+
+          if (usedDep.references[0].resolved !== topScopeRef) {
+            return;
+          } // Is this a destructured prop?
+
+
+          var def = topScopeRef.defs[0];
+
+          if (def == null || def.name == null || def.type !== 'Parameter') {
+            return;
+          } // Was it called in at least one case? Then it's a function.
+
+
+          var isFunctionCall = false;
+          var id;
+
+          for (var _i2 = 0; _i2 < usedDep.references.length; _i2++) {
+            id = usedDep.references[_i2].identifier;
+
+            if (id != null && id.parent != null && (id.parent.type === 'CallExpression' || id.parent.type === 'OptionalCallExpression') && id.parent.callee === id) {
+              isFunctionCall = true;
+              break;
+            }
+          }
+
+          if (!isFunctionCall) {
+            return;
+          } // If it's missing (i.e. in component scope) *and* it's a parameter
+          // then it is definitely coming from props destructuring.
+          // (It could also be props itself but we wouldn't be calling it then.)
+
+
+          missingCallbackDep = missingDep;
+        });
+
+        if (missingCallbackDep !== null) {
+          extraWarning = " If '" + missingCallbackDep + "' changes too often, " + "find the parent component that defines it " + "and wrap that definition in useCallback.";
+        }
+      }
+
+      if (!extraWarning && missingDependencies.size > 0) {
+        var setStateRecommendation = null;
+        missingDependencies.forEach(function (missingDep) {
+          if (setStateRecommendation !== null) {
+            return;
+          }
+
+          var usedDep = dependencies.get(missingDep);
+          var references = usedDep.references;
+          var id;
+          var maybeCall;
+
+          for (var _i3 = 0; _i3 < references.length; _i3++) {
+            id = references[_i3].identifier;
+            maybeCall = id.parent; // Try to see if we have setState(someExpr(missingDep)).
+
+            while (maybeCall != null && maybeCall !== componentScope.block) {
+              if (maybeCall.type === 'CallExpression') {
+                var correspondingStateVariable = setStateCallSites.get(maybeCall.callee);
+
+                if (correspondingStateVariable != null) {
+                  if (correspondingStateVariable.name === missingDep) {
+                    // setCount(count + 1)
+                    setStateRecommendation = {
+                      missingDep: missingDep,
+                      setter: maybeCall.callee.name,
+                      form: 'updater'
+                    };
+                  } else if (stateVariables.has(id)) {
+                    // setCount(count + increment)
+                    setStateRecommendation = {
+                      missingDep: missingDep,
+                      setter: maybeCall.callee.name,
+                      form: 'reducer'
+                    };
+                  } else {
+                    var resolved = references[_i3].resolved;
+
+                    if (resolved != null) {
+                      // If it's a parameter *and* a missing dep,
+                      // it must be a prop or something inside a prop.
+                      // Therefore, recommend an inline reducer.
+                      var def = resolved.defs[0];
+
+                      if (def != null && def.type === 'Parameter') {
+                        setStateRecommendation = {
+                          missingDep: missingDep,
+                          setter: maybeCall.callee.name,
+                          form: 'inlineReducer'
+                        };
+                      }
+                    }
+                  }
+
+                  break;
+                }
+              }
+
+              maybeCall = maybeCall.parent;
+            }
+
+            if (setStateRecommendation !== null) {
+              break;
+            }
+          }
+        });
+
+        if (setStateRecommendation !== null) {
+          switch (setStateRecommendation.form) {
+            case 'reducer':
+              extraWarning = " You can also replace multiple useState variables with useReducer " + ("if '" + setStateRecommendation.setter + "' needs the ") + ("current value of '" + setStateRecommendation.missingDep + "'.");
+              break;
+
+            case 'inlineReducer':
+              extraWarning = " If '" + setStateRecommendation.setter + "' needs the " + ("current value of '" + setStateRecommendation.missingDep + "', ") + "you can also switch to useReducer instead of useState and " + ("read '" + setStateRecommendation.missingDep + "' in the reducer.");
+              break;
+
+            case 'updater':
+              extraWarning = " You can also do a functional update '" + setStateRecommendation.setter + "(" + setStateRecommendation.missingDep.substring(0, 1) + " => ...)' if you only need '" + setStateRecommendation.missingDep + "'" + (" in the '" + setStateRecommendation.setter + "' call.");
+              break;
+
+            default:
+              throw new Error('Unknown case.');
+          }
+        }
+      }
+
+      reportProblem({
+        node: declaredDependenciesNode,
+        message: "React Hook " + context.getSource(reactiveHook) + " has " + ( // To avoid a long message, show the next actionable item.
+        getWarningMessage(missingDependencies, 'a', 'missing', 'include') || getWarningMessage(unnecessaryDependencies, 'an', 'unnecessary', 'exclude') || getWarningMessage(duplicateDependencies, 'a', 'duplicate', 'omit')) + extraWarning,
+        suggest: [{
+          desc: "Update the dependencies array to be: [" + suggestedDeps.map(formatDependency).join(', ') + "]",
+          fix: function (fixer) {
+            // TODO: consider preserving the comments or formatting?
+            return fixer.replaceText(declaredDependenciesNode, "[" + suggestedDeps.map(formatDependency).join(', ') + "]");
+          }
+        }]
+      });
+    }
+
+    function visitCallExpression(node) {
+      var callbackIndex = getReactiveHookCallbackIndex(node.callee, options);
+
+      if (callbackIndex === -1) {
+        // Not a React Hook call that needs deps.
+        return;
+      }
+
+      var callback = node.arguments[callbackIndex];
+      var reactiveHook = node.callee;
+      var reactiveHookName = getNodeWithoutReactNamespace(reactiveHook).name;
+      var declaredDependenciesNode = node.arguments[callbackIndex + 1];
+      var isEffect = /Effect($|[^a-z])/g.test(reactiveHookName); // Check whether a callback is supplied. If there is no callback supplied
+      // then the hook will not work and React will throw a TypeError.
+      // So no need to check for dependency inclusion.
+
+      if (!callback) {
+        reportProblem({
+          node: reactiveHook,
+          message: "React Hook " + reactiveHookName + " requires an effect callback. " + "Did you forget to pass a callback to the hook?"
+        });
+        return;
+      } // Check the declared dependencies for this reactive hook. If there is no
+      // second argument then the reactive callback will re-run on every render.
+      // So no need to check for dependency inclusion.
+
+
+      if (!declaredDependenciesNode && !isEffect) {
+        // These are only used for optimization.
+        if (reactiveHookName === 'useMemo' || reactiveHookName === 'useCallback') {
+          // TODO: Can this have a suggestion?
+          reportProblem({
+            node: reactiveHook,
+            message: "React Hook " + reactiveHookName + " does nothing when called with " + "only one argument. Did you forget to pass an array of " + "dependencies?"
+          });
+        }
+
+        return;
+      }
+
+      switch (callback.type) {
+        case 'FunctionExpression':
+        case 'ArrowFunctionExpression':
+          visitFunctionWithDependencies(callback, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
+          return;
+        // Handled
+
+        case 'Identifier':
+          if (!declaredDependenciesNode) {
+            // No deps, no problems.
+            return; // Handled
+          } // The function passed as a callback is not written inline.
+          // But perhaps it's in the dependencies array?
+
+
+          if (declaredDependenciesNode.elements && declaredDependenciesNode.elements.some(function (el) {
+            return el && el.type === 'Identifier' && el.name === callback.name;
+          })) {
+            // If it's already in the list of deps, we don't care because
+            // this is valid regardless.
+            return; // Handled
+          } // We'll do our best effort to find it, complain otherwise.
+
+
+          var variable = context.getScope().set.get(callback.name);
+
+          if (variable == null || variable.defs == null) {
+            // If it's not in scope, we don't care.
+            return; // Handled
+          } // The function passed as a callback is not written inline.
+          // But it's defined somewhere in the render scope.
+          // We'll do our best effort to find and check it, complain otherwise.
+
+
+          var def = variable.defs[0];
+
+          if (!def || !def.node) {
+            break; // Unhandled
+          }
+
+          if (def.type !== 'Variable' && def.type !== 'FunctionName') {
+            // Parameter or an unusual pattern. Bail out.
+            break; // Unhandled
+          }
+
+          switch (def.node.type) {
+            case 'FunctionDeclaration':
+              // useEffect(() => { ... }, []);
+              visitFunctionWithDependencies(def.node, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
+              return;
+            // Handled
+
+            case 'VariableDeclarator':
+              var init = def.node.init;
+
+              if (!init) {
+                break; // Unhandled
+              }
+
+              switch (init.type) {
+                // const effectBody = () => {...};
+                // useEffect(effectBody, []);
+                case 'ArrowFunctionExpression':
+                case 'FunctionExpression':
+                  // We can inspect this function as if it were inline.
+                  visitFunctionWithDependencies(init, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
+                  return;
+                // Handled
+              }
+
+              break;
+            // Unhandled
+          }
+
+          break;
+        // Unhandled
+
+        default:
+          // useEffect(generateEffectBody(), []);
+          reportProblem({
+            node: reactiveHook,
+            message: "React Hook " + reactiveHookName + " received a function whose dependencies " + "are unknown. Pass an inline function instead."
+          });
+          return;
+        // Handled
+      } // Something unusual. Fall back to suggesting to add the body itself as a dep.
+
+
+      reportProblem({
+        node: reactiveHook,
+        message: "React Hook " + reactiveHookName + " has a missing dependency: '" + callback.name + "'. " + "Either include it or remove the dependency array.",
+        suggest: [{
+          desc: "Update the dependencies array to be: [" + callback.name + "]",
+          fix: function (fixer) {
+            return fixer.replaceText(declaredDependenciesNode, "[" + callback.name + "]");
+          }
+        }]
+      });
+    }
+
+    return {
+      CallExpression: visitCallExpression
+    };
+  }
+}; // The meat of the logic.
+
+function collectRecommendations(_ref6) {
+  var dependencies = _ref6.dependencies,
+      declaredDependencies = _ref6.declaredDependencies,
+      stableDependencies = _ref6.stableDependencies,
+      externalDependencies = _ref6.externalDependencies,
+      isEffect = _ref6.isEffect;
+  // Our primary data structure.
+  // It is a logical representation of property chains:
+  // `props` -> `props.foo` -> `props.foo.bar` -> `props.foo.bar.baz`
+  //         -> `props.lol`
+  //         -> `props.huh` -> `props.huh.okay`
+  //         -> `props.wow`
+  // We'll use it to mark nodes that are *used* by the programmer,
+  // and the nodes that were *declared* as deps. Then we will
+  // traverse it to learn which deps are missing or unnecessary.
+  var depTree = createDepTree();
+
+  function createDepTree() {
+    return {
+      isUsed: false,
+      // True if used in code
+      isSatisfiedRecursively: false,
+      // True if specified in deps
+      isSubtreeUsed: false,
+      // True if something deeper is used by code
+      children: new Map() // Nodes for properties
+
+    };
+  } // Mark all required nodes first.
+  // Imagine exclamation marks next to each used deep property.
+
+
+  dependencies.forEach(function (_, key) {
+    var node = getOrCreateNodeByPath(depTree, key);
+    node.isUsed = true;
+    markAllParentsByPath(depTree, key, function (parent) {
+      parent.isSubtreeUsed = true;
+    });
+  }); // Mark all satisfied nodes.
+  // Imagine checkmarks next to each declared dependency.
+
+  declaredDependencies.forEach(function (_ref7) {
+    var key = _ref7.key;
+    var node = getOrCreateNodeByPath(depTree, key);
+    node.isSatisfiedRecursively = true;
+  });
+  stableDependencies.forEach(function (key) {
+    var node = getOrCreateNodeByPath(depTree, key);
+    node.isSatisfiedRecursively = true;
+  }); // Tree manipulation helpers.
+
+  function getOrCreateNodeByPath(rootNode, path) {
+    var keys = path.split('.');
+    var node = rootNode;
+
+    var _iterator3 = _createForOfIteratorHelper(keys),
+        _step3;
+
+    try {
+      for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
+        var key = _step3.value;
+        var child = node.children.get(key);
+
+        if (!child) {
+          child = createDepTree();
+          node.children.set(key, child);
+        }
+
+        node = child;
+      }
+    } catch (err) {
+      _iterator3.e(err);
+    } finally {
+      _iterator3.f();
+    }
+
+    return node;
+  }
+
+  function markAllParentsByPath(rootNode, path, fn) {
+    var keys = path.split('.');
+    var node = rootNode;
+
+    var _iterator4 = _createForOfIteratorHelper(keys),
+        _step4;
+
+    try {
+      for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
+        var key = _step4.value;
+        var child = node.children.get(key);
+
+        if (!child) {
+          return;
+        }
+
+        fn(child);
+        node = child;
+      }
+    } catch (err) {
+      _iterator4.e(err);
+    } finally {
+      _iterator4.f();
+    }
+  } // Now we can learn which dependencies are missing or necessary.
+
+
+  var missingDependencies = new Set();
+  var satisfyingDependencies = new Set();
+  scanTreeRecursively(depTree, missingDependencies, satisfyingDependencies, function (key) {
+    return key;
+  });
+
+  function scanTreeRecursively(node, missingPaths, satisfyingPaths, keyToPath) {
+    node.children.forEach(function (child, key) {
+      var path = keyToPath(key);
+
+      if (child.isSatisfiedRecursively) {
+        if (child.isSubtreeUsed) {
+          // Remember this dep actually satisfied something.
+          satisfyingPaths.add(path);
+        } // It doesn't matter if there's something deeper.
+        // It would be transitively satisfied since we assume immutability.
+        // `props.foo` is enough if you read `props.foo.id`.
+
+
+        return;
+      }
+
+      if (child.isUsed) {
+        // Remember that no declared deps satisfied this node.
+        missingPaths.add(path); // If we got here, nothing in its subtree was satisfied.
+        // No need to search further.
+
+        return;
+      }
+
+      scanTreeRecursively(child, missingPaths, satisfyingPaths, function (childKey) {
+        return path + '.' + childKey;
+      });
+    });
+  } // Collect suggestions in the order they were originally specified.
+
+
+  var suggestedDependencies = [];
+  var unnecessaryDependencies = new Set();
+  var duplicateDependencies = new Set();
+  declaredDependencies.forEach(function (_ref8) {
+    var key = _ref8.key;
+
+    // Does this declared dep satisfy a real need?
+    if (satisfyingDependencies.has(key)) {
+      if (suggestedDependencies.indexOf(key) === -1) {
+        // Good one.
+        suggestedDependencies.push(key);
+      } else {
+        // Duplicate.
+        duplicateDependencies.add(key);
+      }
+    } else {
+      if (isEffect && !key.endsWith('.current') && !externalDependencies.has(key)) {
+        // Effects are allowed extra "unnecessary" deps.
+        // Such as resetting scroll when ID changes.
+        // Consider them legit.
+        // The exception is ref.current which is always wrong.
+        if (suggestedDependencies.indexOf(key) === -1) {
+          suggestedDependencies.push(key);
+        }
+      } else {
+        // It's definitely not needed.
+        unnecessaryDependencies.add(key);
+      }
+    }
+  }); // Then add the missing ones at the end.
+
+  missingDependencies.forEach(function (key) {
+    suggestedDependencies.push(key);
+  });
+  return {
+    suggestedDependencies: suggestedDependencies,
+    unnecessaryDependencies: unnecessaryDependencies,
+    duplicateDependencies: duplicateDependencies,
+    missingDependencies: missingDependencies
+  };
+} // If the node will result in constructing a referentially unique value, return
+// its human readable type name, else return null.
+
+
+function getConstructionExpressionType(node) {
+  switch (node.type) {
+    case 'ObjectExpression':
+      return 'object';
+
+    case 'ArrayExpression':
+      return 'array';
+
+    case 'ArrowFunctionExpression':
+    case 'FunctionExpression':
+      return 'function';
+
+    case 'ClassExpression':
+      return 'class';
+
+    case 'ConditionalExpression':
+      if (getConstructionExpressionType(node.consequent) != null || getConstructionExpressionType(node.alternate) != null) {
+        return 'conditional';
+      }
+
+      return null;
+
+    case 'LogicalExpression':
+      if (getConstructionExpressionType(node.left) != null || getConstructionExpressionType(node.right) != null) {
+        return 'logical expression';
+      }
+
+      return null;
+
+    case 'JSXFragment':
+      return 'JSX fragment';
+
+    case 'JSXElement':
+      return 'JSX element';
+
+    case 'AssignmentExpression':
+      if (getConstructionExpressionType(node.right) != null) {
+        return 'assignment expression';
+      }
+
+      return null;
+
+    case 'NewExpression':
+      return 'object construction';
+
+    case 'Literal':
+      if (node.value instanceof RegExp) {
+        return 'regular expression';
+      }
+
+      return null;
+
+    case 'TypeCastExpression':
+      return getConstructionExpressionType(node.expression);
+
+    case 'TSAsExpression':
+      return getConstructionExpressionType(node.expression);
+  }
+
+  return null;
+} // Finds variables declared as dependencies
+// that would invalidate on every render.
+
+
+function scanForConstructions(_ref9) {
+  var declaredDependencies = _ref9.declaredDependencies,
+      declaredDependenciesNode = _ref9.declaredDependenciesNode,
+      componentScope = _ref9.componentScope,
+      scope = _ref9.scope;
+  var constructions = declaredDependencies.map(function (_ref10) {
+    var key = _ref10.key;
+    var ref = componentScope.variables.find(function (v) {
+      return v.name === key;
+    });
+
+    if (ref == null) {
+      return null;
+    }
+
+    var node = ref.defs[0];
+
+    if (node == null) {
+      return null;
+    } // const handleChange = function () {}
+    // const handleChange = () => {}
+    // const foo = {}
+    // const foo = []
+    // etc.
+
+
+    if (node.type === 'Variable' && node.node.type === 'VariableDeclarator' && node.node.id.type === 'Identifier' && // Ensure this is not destructed assignment
+    node.node.init != null) {
+      var constantExpressionType = getConstructionExpressionType(node.node.init);
+
+      if (constantExpressionType != null) {
+        return [ref, constantExpressionType];
+      }
+    } // function handleChange() {}
+
+
+    if (node.type === 'FunctionName' && node.node.type === 'FunctionDeclaration') {
+      return [ref, 'function'];
+    } // class Foo {}
+
+
+    if (node.type === 'ClassName' && node.node.type === 'ClassDeclaration') {
+      return [ref, 'class'];
+    }
+
+    return null;
+  }).filter(Boolean);
+
+  function isUsedOutsideOfHook(ref) {
+    var foundWriteExpr = false;
+
+    for (var i = 0; i < ref.references.length; i++) {
+      var reference = ref.references[i];
+
+      if (reference.writeExpr) {
+        if (foundWriteExpr) {
+          // Two writes to the same function.
+          return true;
+        } else {
+          // Ignore first write as it's not usage.
+          foundWriteExpr = true;
+          continue;
+        }
+      }
+
+      var currentScope = reference.from;
+
+      while (currentScope !== scope && currentScope != null) {
+        currentScope = currentScope.upper;
+      }
+
+      if (currentScope !== scope) {
+        // This reference is outside the Hook callback.
+        // It can only be legit if it's the deps array.
+        if (!isAncestorNodeOf(declaredDependenciesNode, reference.identifier)) {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  return constructions.map(function (_ref11) {
+    var ref = _ref11[0],
+        depType = _ref11[1];
+    return {
+      construction: ref.defs[0],
+      depType: depType,
+      isUsedOutsideOfHook: isUsedOutsideOfHook(ref)
+    };
+  });
+}
+/**
+ * Assuming () means the passed/returned node:
+ * (props) => (props)
+ * props.(foo) => (props.foo)
+ * props.foo.(bar) => (props).foo.bar
+ * props.foo.bar.(baz) => (props).foo.bar.baz
+ */
+
+
+function getDependency(node) {
+  if ((node.parent.type === 'MemberExpression' || node.parent.type === 'OptionalMemberExpression') && node.parent.object === node && node.parent.property.name !== 'current' && !node.parent.computed && !(node.parent.parent != null && (node.parent.parent.type === 'CallExpression' || node.parent.parent.type === 'OptionalCallExpression') && node.parent.parent.callee === node.parent)) {
+    return getDependency(node.parent);
+  } else if ( // Note: we don't check OptionalMemberExpression because it can't be LHS.
+  node.type === 'MemberExpression' && node.parent && node.parent.type === 'AssignmentExpression' && node.parent.left === node) {
+    return node.object;
+  } else {
+    return node;
+  }
+}
+/**
+ * Mark a node as either optional or required.
+ * Note: If the node argument is an OptionalMemberExpression, it doesn't necessarily mean it is optional.
+ * It just means there is an optional member somewhere inside.
+ * This particular node might still represent a required member, so check .optional field.
+ */
+
+
+function markNode(node, optionalChains, result) {
+  if (optionalChains) {
+    if (node.optional) {
+      // We only want to consider it optional if *all* usages were optional.
+      if (!optionalChains.has(result)) {
+        // Mark as (maybe) optional. If there's a required usage, this will be overridden.
+        optionalChains.set(result, true);
+      }
+    } else {
+      // Mark as required.
+      optionalChains.set(result, false);
+    }
+  }
+}
+/**
+ * Assuming () means the passed node.
+ * (foo) -> 'foo'
+ * foo(.)bar -> 'foo.bar'
+ * foo.bar(.)baz -> 'foo.bar.baz'
+ * Otherwise throw.
+ */
+
+
+function analyzePropertyChain(node, optionalChains) {
+  if (node.type === 'Identifier' || node.type === 'JSXIdentifier') {
+    var result = node.name;
+
+    if (optionalChains) {
+      // Mark as required.
+      optionalChains.set(result, false);
+    }
+
+    return result;
+  } else if (node.type === 'MemberExpression' && !node.computed) {
+    var object = analyzePropertyChain(node.object, optionalChains);
+    var property = analyzePropertyChain(node.property, null);
+
+    var _result = object + "." + property;
+
+    markNode(node, optionalChains, _result);
+    return _result;
+  } else if (node.type === 'OptionalMemberExpression' && !node.computed) {
+    var _object = analyzePropertyChain(node.object, optionalChains);
+
+    var _property = analyzePropertyChain(node.property, null);
+
+    var _result2 = _object + "." + _property;
+
+    markNode(node, optionalChains, _result2);
+    return _result2;
+  } else if (node.type === 'ChainExpression' && !node.computed) {
+    var expression = node.expression;
+
+    if (expression.type === 'CallExpression') {
+      throw new Error("Unsupported node type: " + expression.type);
+    }
+
+    var _object2 = analyzePropertyChain(expression.object, optionalChains);
+
+    var _property2 = analyzePropertyChain(expression.property, null);
+
+    var _result3 = _object2 + "." + _property2;
+
+    markNode(expression, optionalChains, _result3);
+    return _result3;
+  } else {
+    throw new Error("Unsupported node type: " + node.type);
+  }
+}
+
+function getNodeWithoutReactNamespace(node, options) {
+  if (node.type === 'MemberExpression' && node.object.type === 'Identifier' && node.object.name === 'React' && node.property.type === 'Identifier' && !node.computed) {
+    return node.property;
+  }
+
+  return node;
+} // What's the index of callback that needs to be analyzed for a given Hook?
+// -1 if it's not a Hook we care about (e.g. useState).
+// 0 for useEffect/useMemo/useCallback(fn).
+// 1 for useImperativeHandle(ref, fn).
+// For additionally configured Hooks, assume that they're like useEffect (0).
+
+
+function getReactiveHookCallbackIndex(calleeNode, options) {
+  var node = getNodeWithoutReactNamespace(calleeNode);
+
+  if (node.type !== 'Identifier') {
+    return -1;
+  }
+
+  switch (node.name) {
+    case 'useEffect':
+    case 'useLayoutEffect':
+    case 'useCallback':
+    case 'useMemo':
+      // useEffect(fn)
+      return 0;
+
+    case 'useImperativeHandle':
+      // useImperativeHandle(ref, fn)
+      return 1;
+
+    default:
+      if (node === calleeNode && options && options.additionalHooks) {
+        // Allow the user to provide a regular expression which enables the lint to
+        // target custom reactive hooks.
+        var name;
+
+        try {
+          name = analyzePropertyChain(node, null);
+        } catch (error) {
+          if (/Unsupported node type/.test(error.message)) {
+            return 0;
+          } else {
+            throw error;
+          }
+        }
+
+        return options.additionalHooks.test(name) ? 0 : -1;
+      } else {
+        return -1;
+      }
+
+  }
+}
+/**
+ * ESLint won't assign node.parent to references from context.getScope()
+ *
+ * So instead we search for the node from an ancestor assigning node.parent
+ * as we go. This mutates the AST.
+ *
+ * This traversal is:
+ * - optimized by only searching nodes with a range surrounding our target node
+ * - agnostic to AST node types, it looks for `{ type: string, ... }`
+ */
+
+
+function fastFindReferenceWithParent(start, target) {
+  var queue = [start];
+  var item = null;
+
+  while (queue.length) {
+    item = queue.shift();
+
+    if (isSameIdentifier(item, target)) {
+      return item;
+    }
+
+    if (!isAncestorNodeOf(item, target)) {
+      continue;
+    }
+
+    for (var _i4 = 0, _Object$entries = Object.entries(item); _i4 < _Object$entries.length; _i4++) {
+      var _Object$entries$_i = _Object$entries[_i4],
+          key = _Object$entries$_i[0],
+          value = _Object$entries$_i[1];
+
+      if (key === 'parent') {
+        continue;
+      }
+
+      if (isNodeLike(value)) {
+        value.parent = item;
+        queue.push(value);
+      } else if (Array.isArray(value)) {
+        value.forEach(function (val) {
+          if (isNodeLike(val)) {
+            val.parent = item;
+            queue.push(val);
+          }
+        });
+      }
+    }
+  }
+
+  return null;
+}
+
+function joinEnglish(arr) {
+  var s = '';
+
+  for (var i = 0; i < arr.length; i++) {
+    s += arr[i];
+
+    if (i === 0 && arr.length === 2) {
+      s += ' and ';
+    } else if (i === arr.length - 2 && arr.length > 2) {
+      s += ', and ';
+    } else if (i < arr.length - 1) {
+      s += ', ';
+    }
+  }
+
+  return s;
+}
+
+function isNodeLike(val) {
+  return typeof val === 'object' && val !== null && !Array.isArray(val) && typeof val.type === 'string';
+}
+
+function isSameIdentifier(a, b) {
+  return (a.type === 'Identifier' || a.type === 'JSXIdentifier') && a.type === b.type && a.name === b.name && a.range[0] === b.range[0] && a.range[1] === b.range[1];
+}
+
+function isAncestorNodeOf(a, b) {
+  return a.range[0] <= b.range[0] && a.range[1] >= b.range[1];
+}
+
+var configs = {
+  recommended: {
+    plugins: ['react-hooks'],
+    rules: {
+      'react-hooks/rules-of-hooks': 'error',
+      'react-hooks/exhaustive-deps': 'warn'
+    }
+  }
+};
+var rules = {
+  'rules-of-hooks': RulesOfHooks,
+  'exhaustive-deps': ExhaustiveDeps
+};
+
+exports.configs = configs;
+exports.rules = rules;
+  })();
+}
Index: frontend/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.production.min.js
===================================================================
--- frontend/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.production.min.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.production.min.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,63 @@
+/**
+ * @license React
+ * eslint-plugin-react-hooks.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+'use strict';function ca(a,c){if(a){if("string"===typeof a)return da(a,c);var b=Object.prototype.toString.call(a).slice(8,-1);"Object"===b&&a.constructor&&(b=a.constructor.name);if("Map"===b||"Set"===b)return Array.from(a);if("Arguments"===b||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(b))return da(a,c)}}function da(a,c){if(null==c||c>a.length)c=a.length;for(var b=0,d=Array(c);b<c;b++)d[b]=a[b];return d}
+function D(a,c){var b;if("undefined"===typeof Symbol||null==a[Symbol.iterator]){if(Array.isArray(a)||(b=ca(a))||c&&a&&"number"===typeof a.length){b&&(a=b);var d=0;c=function(){};return{s:c,n:function(){return d>=a.length?{done:!0}:{done:!1,value:a[d++]}},e:function(a){throw a;},f:c}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");}var f=!0,p=!1,g;return{s:function(){b=a[Symbol.iterator]()},
+n:function(){var a=b.next();f=a.done;return a},e:function(a){p=!0;g=a},f:function(){try{f||null==b.return||b.return()}finally{if(p)throw g;}}}}function G(a){if("Identifier"===a.type)return/^use[A-Z0-9].*$/.test(a.name);if("MemberExpression"===a.type&&!a.computed&&G(a.property)){a=a.object;var c=/^[A-Z].*/;return"Identifier"===a.type&&c.test(a.name)}return!1}function ea(a){return"Identifier"===a.type?!/^[a-z]/.test(a.name):!1}
+function fa(a,c){return a.name===c||"MemberExpression"===a.type&&"React"===a.object.name&&a.property.name===c}function ha(a){return!!(a.parent&&a.parent.callee&&fa(a.parent.callee,"forwardRef"))}function ia(a){return!!(a.parent&&a.parent.callee&&fa(a.parent.callee,"memo"))}function ma(a){for(;a;){var c=na(a);if(c&&(ea(c)||G(c))||ha(a)||ia(a))return!0;a=a.parent}return!1}
+function na(a){if("FunctionDeclaration"===a.type||"FunctionExpression"===a.type&&a.id)return a.id;if("FunctionExpression"===a.type||"ArrowFunctionExpression"===a.type)return"VariableDeclarator"===a.parent.type&&a.parent.init===a?a.parent.id:"AssignmentExpression"===a.parent.type&&a.parent.right===a&&"="===a.parent.operator?a.parent.left:"Property"!==a.parent.type||a.parent.value!==a||a.parent.computed?"AssignmentPattern"!==a.parent.type||a.parent.right!==a||a.parent.computed?void 0:a.parent.left:
+a.parent.key}
+function H(a){function c(){return{isUsed:!1,isSatisfiedRecursively:!1,isSubtreeUsed:!1,children:new Map}}function b(a,b){b=b.split(".");b=D(b);var h;try{for(b.s();!(h=b.n()).done;){var d=h.value,f=a.children.get(d);f||(f=c(),a.children.set(d,f));a=f}}catch(F){b.e(F)}finally{b.f()}return a}function d(a,c,b){c=c.split(".");c=D(c);var d;try{for(c.s();!(d=c.n()).done;){var h=a.children.get(d.value);if(!h)break;b(h);a=h}}catch(F){c.e(F)}finally{c.f()}}function f(a,c,b,d){a.children.forEach(function(a,h){var e=
+d(h);a.isSatisfiedRecursively?a.isSubtreeUsed&&b.add(e):a.isUsed?c.add(e):f(a,c,b,function(a){return e+"."+a})})}var p=a.dependencies,g=a.declaredDependencies,w=a.stableDependencies,m=a.externalDependencies,E=a.isEffect,x=c();p.forEach(function(a,c){b(x,c).isUsed=!0;d(x,c,function(a){a.isSubtreeUsed=!0})});g.forEach(function(a){b(x,a.key).isSatisfiedRecursively=!0});w.forEach(function(a){b(x,a).isSatisfiedRecursively=!0});a=new Set;var r=new Set;f(x,a,r,function(a){return a});var e=[],C=new Set,t=
+new Set;g.forEach(function(a){a=a.key;r.has(a)?-1===e.indexOf(a)?e.push(a):t.add(a):!E||a.endsWith(".current")||m.has(a)?C.add(a):-1===e.indexOf(a)&&e.push(a)});a.forEach(function(a){e.push(a)});return{suggestedDependencies:e,unnecessaryDependencies:C,duplicateDependencies:t,missingDependencies:a}}
+function Q(a){switch(a.type){case "ObjectExpression":return"object";case "ArrayExpression":return"array";case "ArrowFunctionExpression":case "FunctionExpression":return"function";case "ClassExpression":return"class";case "ConditionalExpression":if(null!=Q(a.consequent)||null!=Q(a.alternate))return"conditional";break;case "LogicalExpression":if(null!=Q(a.left)||null!=Q(a.right))return"logical expression";break;case "JSXFragment":return"JSX fragment";case "JSXElement":return"JSX element";case "AssignmentExpression":if(null!=
+Q(a.right))return"assignment expression";break;case "NewExpression":return"object construction";case "Literal":if(a.value instanceof RegExp)return"regular expression";break;case "TypeCastExpression":return Q(a.expression);case "TSAsExpression":return Q(a.expression)}return null}
+function oa(a){var c=a.declaredDependenciesNode,b=a.componentScope,d=a.scope;return a.declaredDependencies.map(function(a){var c=a.key;a=b.variables.find(function(a){return a.name===c});if(null==a)return null;var d=a.defs[0];if(null==d)return null;if("Variable"===d.type&&"VariableDeclarator"===d.node.type&&"Identifier"===d.node.id.type&&null!=d.node.init){var f=Q(d.node.init);if(null!=f)return[a,f]}return"FunctionName"===d.type&&"FunctionDeclaration"===d.node.type?[a,"function"]:"ClassName"===d.type&&
+"ClassDeclaration"===d.node.type?[a,"class"]:null}).filter(Boolean).map(function(a){var b=a[0];a=a[1];var f=b.defs[0];a:{for(var w=!1,m=0;m<b.references.length;m++){var E=b.references[m];if(E.writeExpr)if(w){b=!0;break a}else{w=!0;continue}for(var x=E.from;x!==d&&null!=x;)x=x.upper;if(x!==d&&!pa(c,E.identifier)){b=!0;break a}}b=!1}return{construction:f,depType:a,isUsedOutsideOfHook:b}})}
+function sa(a){return"MemberExpression"!==a.parent.type&&"OptionalMemberExpression"!==a.parent.type||a.parent.object!==a||"current"===a.parent.property.name||a.parent.computed||null!=a.parent.parent&&("CallExpression"===a.parent.parent.type||"OptionalCallExpression"===a.parent.parent.type)&&a.parent.parent.callee===a.parent?"MemberExpression"===a.type&&a.parent&&"AssignmentExpression"===a.parent.type&&a.parent.left===a?a.object:a:sa(a.parent)}
+function W(a,c,b){c&&(a.optional?c.has(b)||c.set(b,!0):c.set(b,!1))}
+function X(a,c){if("Identifier"===a.type||"JSXIdentifier"===a.type)return a=a.name,c&&c.set(a,!1),a;if("MemberExpression"!==a.type||a.computed){if("OptionalMemberExpression"!==a.type||a.computed){if("ChainExpression"!==a.type||a.computed)throw Error("Unsupported node type: "+a.type);a=a.expression;if("CallExpression"===a.type)throw Error("Unsupported node type: "+a.type);var b=X(a.object,c),d=X(a.property,null);b=b+"."+d;W(a,c,b);return b}b=X(a.object,c);d=X(a.property,null);b=b+"."+d;W(a,c,b);return b}b=
+X(a.object,c);d=X(a.property,null);b=b+"."+d;W(a,c,b);return b}function ta(a){return"MemberExpression"!==a.type||"Identifier"!==a.object.type||"React"!==a.object.name||"Identifier"!==a.property.type||a.computed?a:a.property}
+function ua(a,c){var b=ta(a);if("Identifier"!==b.type)return-1;switch(b.name){case "useEffect":case "useLayoutEffect":case "useCallback":case "useMemo":return 0;case "useImperativeHandle":return 1;default:if(b===a&&c&&c.additionalHooks){try{var d=X(b,null)}catch(f){if(/Unsupported node type/.test(f.message))return 0;throw f;}return c.additionalHooks.test(d)?0:-1}return-1}}
+function Y(a,c){for(var b=[a],d=null;b.length;){d=b.shift();if(("Identifier"===d.type||"JSXIdentifier"===d.type)&&d.type===c.type&&d.name===c.name&&d.range[0]===c.range[0]&&d.range[1]===c.range[1])return d;if(pa(d,c)){a=0;for(var f=Object.entries(d);a<f.length;a++){var p=f[a],g=p[1];"parent"!==p[0]&&(va(g)?(g.parent=d,b.push(g)):Array.isArray(g)&&g.forEach(function(a){va(a)&&(a.parent=d,b.push(a))}))}}}return null}
+function wa(a){for(var c="",b=0;b<a.length;b++)c+=a[b],0===b&&2===a.length?c+=" and ":b===a.length-2&&2<a.length?c+=", and ":b<a.length-1&&(c+=", ");return c}function va(a){return"object"===typeof a&&null!==a&&!Array.isArray(a)&&"string"===typeof a.type}function pa(a,c){return a.range[0]<=c.range[0]&&a.range[1]>=c.range[1]}exports.configs={recommended:{plugins:["react-hooks"],rules:{"react-hooks/rules-of-hooks":"error","react-hooks/exhaustive-deps":"warn"}}};
+exports.rules={"rules-of-hooks":{meta:{type:"problem",docs:{description:"enforces the Rules of Hooks",recommended:!0,url:"https://reactjs.org/docs/hooks-rules.html"}},create:function(a){var c=[],b=[];return{onCodePathSegmentStart:function(a){return b.push(a)},onCodePathSegmentEnd:function(){return b.pop()},onCodePathStart:function(){return c.push(new Map)},onCodePathEnd:function(b,f){function d(a,c){var e=d.cache,f=e.get(a.id);c=new Set(c);if(c.has(a.id)){e=[].concat(c);a=e.slice(e.indexOf(a.id)+
+1);a=D(a);var l;try{for(a.s();!(l=a.n()).done;)E.add(l.value)}catch(u){a.e(u)}finally{a.f()}return BigInt("0")}c.add(a.id);if(void 0!==f)return f;if(b.thrownSegments.includes(a))f=BigInt("0");else if(0===a.prevSegments.length)f=BigInt("1");else{f=BigInt("0");l=D(a.prevSegments);var r;try{for(l.s();!(r=l.n()).done;)f+=d(r.value,c)}catch(u){l.e(u)}finally{l.f()}}a.reachable&&f===BigInt("0")?e.delete(a.id):e.set(a.id,f);return f}function g(a,c){var d=g.cache,e=d.get(a.id);c=new Set(c);if(c.has(a.id)){d=
+Array.from(c);a=d.slice(d.indexOf(a.id)+1);a=D(a);var f;try{for(a.s();!(f=a.n()).done;)E.add(f.value)}catch(u){a.e(u)}finally{a.f()}return BigInt("0")}c.add(a.id);if(void 0!==e)return e;if(b.thrownSegments.includes(a))e=BigInt("0");else if(0===a.nextSegments.length)e=BigInt("1");else{e=BigInt("0");f=D(a.nextSegments);var r;try{for(f.s();!(r=f.n()).done;)e+=g(r.value,c)}catch(u){f.e(u)}finally{f.f()}}d.set(a.id,e);return e}function w(a){var c=w.cache,b=c.get(a.id);if(null===b)return Infinity;if(void 0!==
+b)return b;c.set(a.id,null);if(0===a.prevSegments.length)b=1;else{b=Infinity;var d=D(a.prevSegments),e;try{for(d.s();!(e=d.n()).done;){var f=w(e.value);f<b&&(b=f)}}catch(u){d.e(u)}finally{d.f()}b+=1}c.set(a.id,b);return b}var m=c.pop();if(0!==m.size){var E=new Set;d.cache=new Map;g.cache=new Map;w.cache=new Map;var x=g(b.initialSegment),r=na(f),e=ma(f),C=r?ea(r)||G(r):ha(f)||ia(f),t=Infinity,h=D(b.finalSegments),ja;try{for(h.s();!(ja=h.n()).done;){var ka=ja.value;if(ka.reachable){var la=w(ka);la<
+t&&(t=la)}}}catch(l){h.e(l)}finally{h.f()}m=D(m);var A;try{for(m.s();!(A=m.n()).done;){var F=A.value,q=F[0],B=F[1];if(q.reachable){var N=0===q.nextSegments.length?t<=w(q):t<w(q),T=d(q)*g(q),U=E.has(q.id),J=D(B),y;try{for(J.s();!(y=J.n()).done;){var z=y.value;U&&a.report({node:z,message:'React Hook "'+a.getSource(z)+'" may be executed more than once. Possibly because it is called in a loop. React Hooks must be called in the exact same order in every component render.'});if(C){if(!U&&T!==x){var O='React Hook "'+
+a.getSource(z)+'" is called conditionally. React Hooks must be called in the exact same order in every component render.'+(N?" Did you accidentally call a React Hook after an early return?":"");a.report({node:z,message:O})}}else if(f.parent&&("MethodDefinition"===f.parent.type||"ClassProperty"===f.parent.type)&&f.parent.value===f){var M='React Hook "'+a.getSource(z)+'" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.';
+a.report({node:z,message:M})}else if(r){var K='React Hook "'+a.getSource(z)+'" is called in function "'+(a.getSource(r)+'" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".');a.report({node:z,message:K})}else if("Program"===f.type){var L='React Hook "'+a.getSource(z)+'" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.';
+a.report({node:z,message:L})}else if(e){var n='React Hook "'+a.getSource(z)+'" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.';a.report({node:z,message:n})}}}catch(l){J.e(l)}finally{J.f()}}}}catch(l){m.e(l)}finally{m.f()}}},CallExpression:function(a){if(G(a.callee)){var d=c[c.length-1],p=b[b.length-1],g=d.get(p);g||(g=[],d.set(p,g));g.push(a.callee)}}}}},"exhaustive-deps":{meta:{type:"suggestion",docs:{description:"verifies the list of dependencies for Hooks like useEffect and similar",
+recommended:!0,url:"https://github.com/facebook/react/issues/14920"},fixable:"code",hasSuggestions:!0,schema:[{type:"object",additionalProperties:!1,enableDangerousAutofixThisMayCauseInfiniteLoops:!1,properties:{additionalHooks:{type:"string"},enableDangerousAutofixThisMayCauseInfiniteLoops:{type:"boolean"}}}]},create:function(a){function c(c){f&&Array.isArray(c.suggest)&&0<c.suggest.length&&(c.fix=c.suggest[0].fix);a.report(c)}function b(a,c){return function(b){if(c.has(b))return c.get(b);var d=
+a(b);c.set(b,d);return d}}function d(d,e,f,t,h){function r(a){var c=D(a.references),b;try{for(c.s();!(b=c.n()).done;){var k=b.value;if(k.resolved&&F.has(k.resolved.scope)){var f=Y(d,k.identifier),e=sa(f),g=X(e,z),t;if(t=h&&"Identifier"===e.type&&("MemberExpression"===e.parent.type||"OptionalMemberExpression"===e.parent.type)&&!e.parent.computed&&"Identifier"===e.parent.property.type&&"current"===e.parent.property.name){for(var l=k.from,n=!1;l.block!==d;)"function"===l.type&&(n=null!=l.block.parent&&
+"ReturnStatement"===l.block.parent.type),l=l.upper;t=n}t&&J.set(g,{reference:k,dependencyNode:e});if("TSTypeQuery"!==e.parent.type&&"TSTypeReference"!==e.parent.type){var C=k.resolved.defs[0];if(null!=C&&(null==C.node||C.node.init!==d.parent)&&"TypeParameter"!==C.type)if(y.has(g))y.get(g).references.push(k);else{var p=k.resolved,q=T(p)||U(p);y.set(g,{isStable:q,references:[k]})}}}}}catch(aa){c.e(aa)}finally{c.f()}a=D(a.childScopes);var m;try{for(a.s();!(m=a.n()).done;)r(m.value)}catch(aa){a.e(aa)}finally{a.f()}}
+function C(a){a=a.split(".");for(var c="",b=0;b<a.length;b++){if(0!==b){var k=a.slice(0,b+1).join(".");k=!0===z.get(k);c+=k?"?.":"."}c+=a[b]}return c}function p(a,c,b,d){return 0===a.size?null:(1<a.size?"":c+" ")+b+" "+(1<a.size?"dependencies":"dependency")+": "+wa(Array.from(a).sort().map(function(a){return"'"+C(a)+"'"}))+(". Either "+d+" "+(1<a.size?"them":"it")+" or remove the dependency array.")}h&&d.async&&c({node:d,message:"Effect callbacks are synchronous to prevent race conditions. Put the async function inside:\n\nuseEffect(() => {\n  async function fetchData() {\n    // You can await here\n    const response = await MyAPI.getData(someId);\n    // ...\n  }\n  fetchData();\n}, [someId]); // Or [] if effect doesn't need props or state\n\nLearn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching"});
+for(var A=g.acquire(d),F=new Set,q=null,B=A.upper;B;){F.add(B);if("function"===B.type)break;B=B.upper}if(B){q=B;var N=Array.isArray,T=b(function(a){if(!N(a.defs))return!1;var c=a.defs[0];if(null==c||"VariableDeclarator"!==c.node.type)return!1;var b=c.node.init;if(null==b)return!1;for(;"TSAsExpression"===b.type;)b=b.expression;var d=c.node.parent;if(null==d&&(Y(q.block,c.node.id),d=c.node.parent,null==d))return!1;if("const"===d.kind&&"Literal"===b.type&&("string"===typeof b.value||"number"===typeof b.value||
+null===b.value))return!0;if("CallExpression"!==b.type)return!1;b=b.callee;"MemberExpression"!==b.type||"React"!==b.object.name||null==b.property||b.computed||(b=b.property);if("Identifier"!==b.type)return!1;c=c.node.id;b=b.name;if("useRef"===b&&"Identifier"===c.type)return!0;if("useState"===b||"useReducer"===b){if("ArrayPattern"===c.type&&2===c.elements.length&&N(a.identifiers)){if(c.elements[1]===a.identifiers[0]){if("useState"===b)for(a=a.references,d=b=0;d<a.length;d++){a[d].isWrite()&&b++;if(1<
+b)return!1;w.set(a[d].identifier,c.elements[0])}return!0}if(c.elements[0]===a.identifiers[0]&&"useState"===b)for(a=a.references,c=0;c<a.length;c++)m.add(a[c].identifier)}}else if("useTransition"===b&&"ArrayPattern"===c.type&&2===c.elements.length&&Array.isArray(a.identifiers)&&c.elements[1]===a.identifiers[0])return!0;return!1},E),U=b(function(a){if(!N(a.defs))return!1;a=a.defs[0];if(null==a||null==a.node||null==a.node.id)return!1;var c=a.node,b=q.childScopes;a=null;var d;for(d=0;d<b.length;d++){var e=
+b[d],k=e.block;if("FunctionDeclaration"===c.type&&k===c||"VariableDeclarator"===c.type&&k.parent===c){a=e;break}}if(null==a)return!1;for(d=0;d<a.through.length;d++)if(c=a.through[d],null!=c.resolved&&F.has(c.resolved.scope)&&!T(c.resolved))return!1;return!0},x),J=new Map,y=new Map,z=new Map;r(A);J.forEach(function(a,b){var d=a.dependencyNode;a=a.reference.resolved.references;for(var e=!1,f=0;f<a.length;f++){var k=a[f].identifier.parent;if(null!=k&&"MemberExpression"===k.type&&!k.computed&&"Identifier"===
+k.property.type&&"current"===k.property.name&&"AssignmentExpression"===k.parent.type&&k.parent.left===k){e=!0;break}}e||c({node:d.parent.property,message:"The ref value '"+b+".current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy '"+(b+".current' to a variable inside the effect, and use that variable in the cleanup function.")})});var O=new Set,M=new Set;y.forEach(function(b,d){var e=b.references;b.isStable&&M.add(d);
+e.forEach(function(b){b.writeExpr&&(b=b.writeExpr,O.has(d)||(O.add(d),c({node:b,message:"Assignments to the '"+d+"' variable from inside React Hook "+(a.getSource(f)+" will be lost after each render. To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property. Otherwise, you can move this variable directly inside ")+(a.getSource(f)+".")})))})});if(!(0<O.size))if(e){var K=[],L=new Set;"ArrayExpression"!==e.type?c({node:e,message:"React Hook "+a.getSource(f)+
+" was passed a dependency list that is not an array literal. This means we can't statically verify whether you've passed the correct dependencies."}):e.elements.forEach(function(b){if(null!==b)if("SpreadElement"===b.type)c({node:b,message:"React Hook "+a.getSource(f)+" has a spread element in its dependency array. This means we can't statically verify whether you've passed the correct dependencies."});else{try{var d=X(b,null)}catch(ra){if(/Unsupported node type/.test(ra.message)){"Literal"===b.type?
+y.has(b.value)?c({node:b,message:"The "+b.raw+" literal is not a valid dependency because it never changes. Did you mean to include "+(b.value+" in the array instead?")}):c({node:b,message:"The "+b.raw+" literal is not a valid dependency because it never changes. You can safely remove it."}):c({node:b,message:"React Hook "+a.getSource(f)+" has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked."});return}throw ra;}for(var e=b;"MemberExpression"===
+e.type||"OptionalMemberExpression"===e.type||"ChainExpression"===e.type;)e=e.object||e.expression.object;var k=!q.through.some(function(a){return a.identifier===e});K.push({key:d,node:b});k||L.add(d)}});var n=H({dependencies:y,declaredDependencies:K,stableDependencies:M,externalDependencies:L,isEffect:h});B=n.unnecessaryDependencies;var l=n.missingDependencies,qa=n.duplicateDependencies,R=n.suggestedDependencies;if(0===qa.size+l.size+B.size)oa({declaredDependencies:K,declaredDependenciesNode:e,componentScope:q,
+scope:A}).forEach(function(a){var b=a.construction,d=a.isUsedOutsideOfHook;a=a.depType;var f="function"===a?"useCallback":"useMemo",k="function"===a?"definition":"initialization",h="wrap the "+k+" of '"+b.name.name+"' in its own "+f+"() Hook.";h="The '"+b.name.name+"' "+a+" "+("conditional"===a||"logical expression"===a?"could make":"makes")+" the dependencies of "+(t+" Hook (at line "+e.loc.start.line+") change on every render. ")+(d?"To fix this, "+h:"Move it inside the "+t+" callback. Alternatively, "+
+h);var g;d&&"Variable"===b.type&&"function"===a&&(g=[{desc:"Wrap the "+k+" of '"+b.name.name+"' in its own "+f+"() Hook.",fix:function(a){var c="useMemo"===f?["useMemo(() => { return ","; })"]:["useCallback(",")"],d=c[1];return[a.insertTextBefore(b.node.init,c[0]),a.insertTextAfter(b.node.init,d)]}}]);c({node:b.node,message:h,suggest:g})});else{!h&&0<l.size&&(R=H({dependencies:y,declaredDependencies:[],stableDependencies:M,externalDependencies:L,isEffect:h}).suggestedDependencies);(function(){if(0===
+K.length)return!0;var a=K.map(function(a){return a.key}),b=a.slice().sort();return a.join(",")===b.join(",")})()&&R.sort();n="";if(0<B.size){var S=null;Array.from(B.keys()).forEach(function(a){null===S&&a.endsWith(".current")&&(S=a)});if(null!==S)n=" Mutable values like '"+S+"' aren't valid dependencies because mutating them doesn't re-render the component.";else if(0<L.size){var I=Array.from(L)[0];A.set.has(I)||(n=" Outer scope values like '"+I+"' aren't valid dependencies because mutating them doesn't re-render the component.")}}if(!n&&
+l.has("props")){A=y.get("props");if(null==A)return;A=A.references;if(!Array.isArray(A))return;I=!0;for(var Z=0;Z<A.length;Z++){var u=Y(q.block,A[Z].identifier);if(!u){I=!1;break}u=u.parent;if(null==u){I=!1;break}if("MemberExpression"!==u.type&&"OptionalMemberExpression"!==u.type){I=!1;break}}I&&(n=" However, 'props' will change when *any* prop changes, so the preferred fix is to destructure the 'props' object outside of the "+(t+" call and refer to those specific props inside ")+(a.getSource(f)+"."))}if(!n&&
+0<l.size){var V=null;l.forEach(function(a){if(!V){var b=q.set.get(a),c=y.get(a);if(c.references[0].resolved===b&&(b=b.defs[0],null!=b&&null!=b.name&&"Parameter"===b.type)){b=!1;for(var d,e=0;e<c.references.length;e++)if(d=c.references[e].identifier,null!=d&&null!=d.parent&&("CallExpression"===d.parent.type||"OptionalCallExpression"===d.parent.type)&&d.parent.callee===d){b=!0;break}b&&(V=a)}}});null!==V&&(n=" If '"+V+"' changes too often, find the parent component that defines it and wrap that definition in useCallback.")}if(!n&&
+0<l.size){var v=null;l.forEach(function(a){if(null===v)for(var b=y.get(a).references,c,d,e=0;e<b.length;e++){c=b[e].identifier;for(d=c.parent;null!=d&&d!==q.block;){if("CallExpression"===d.type){var f=w.get(d.callee);if(null!=f){f.name===a?v={missingDep:a,setter:d.callee.name,form:"updater"}:m.has(c)?v={missingDep:a,setter:d.callee.name,form:"reducer"}:(c=b[e].resolved,null!=c&&(c=c.defs[0],null!=c&&"Parameter"===c.type&&(v={missingDep:a,setter:d.callee.name,form:"inlineReducer"})));break}}d=d.parent}if(null!==
+v)break}});if(null!==v)switch(v.form){case "reducer":n=" You can also replace multiple useState variables with useReducer if '"+(v.setter+"' needs the current value of '")+(v.missingDep+"'.");break;case "inlineReducer":n=" If '"+v.setter+"' needs the current value of '"+(v.missingDep+"', you can also switch to useReducer instead of useState and read '")+(v.missingDep+"' in the reducer.");break;case "updater":n=" You can also do a functional update '"+v.setter+"("+v.missingDep.substring(0,1)+" => ...)' if you only need '"+
+v.missingDep+"' in the '"+(v.setter+"' call.");break;default:throw Error("Unknown case.");}}c({node:e,message:"React Hook "+a.getSource(f)+" has "+(p(l,"a","missing","include")||p(B,"an","unnecessary","exclude")||p(qa,"a","duplicate","omit"))+n,suggest:[{desc:"Update the dependencies array to be: ["+R.map(C).join(", ")+"]",fix:function(a){return a.replaceText(e,"["+R.map(C).join(", ")+"]")}}]})}}else{var P=null;y.forEach(function(a,b){P||a.references.forEach(function(a){if(!P&&w.has(a.identifier)){for(a=
+a.from;"function"!==a.type;)a=a.upper;a.block===d&&(P=b)}})});if(P){var ba=H({dependencies:y,declaredDependencies:[],stableDependencies:M,externalDependencies:new Set,isEffect:!0}).suggestedDependencies;c({node:f,message:"React Hook "+t+" contains a call to '"+P+"'. Without a list of dependencies, this can lead to an infinite chain of updates. To fix this, pass ["+ba.join(", ")+("] as a second argument to the "+t+" Hook."),suggest:[{desc:"Add dependencies array: ["+ba.join(", ")+"]",fix:function(a){return a.insertTextAfter(d,
+", ["+ba.join(", ")+"]")}}]})}}}}var f=a.options&&a.options[0]&&a.options[0].enableDangerousAutofixThisMayCauseInfiniteLoops||!1,p={additionalHooks:a.options&&a.options[0]&&a.options[0].additionalHooks?new RegExp(a.options[0].additionalHooks):void 0,enableDangerousAutofixThisMayCauseInfiniteLoops:f},g=a.getSourceCode().scopeManager,w=new WeakMap,m=new WeakSet,E=new WeakMap,x=new WeakMap;return{CallExpression:function(b){var e=ua(b.callee,p);if(-1!==e){var f=b.arguments[e],g=b.callee,h=ta(g).name,
+m=b.arguments[e+1];b=/Effect($|[^a-z])/g.test(h);if(f)if(m||b){switch(f.type){case "FunctionExpression":case "ArrowFunctionExpression":d(f,m,g,h,b);return;case "Identifier":if(!m||m.elements&&m.elements.some(function(a){return a&&"Identifier"===a.type&&a.name===f.name}))return;e=a.getScope().set.get(f.name);if(null==e||null==e.defs)return;e=e.defs[0];if(!e||!e.node)break;if("Variable"!==e.type&&"FunctionName"!==e.type)break;switch(e.node.type){case "FunctionDeclaration":d(e.node,m,g,h,b);return;case "VariableDeclarator":if(e=
+e.node.init)switch(e.type){case "ArrowFunctionExpression":case "FunctionExpression":d(e,m,g,h,b);return}}break;default:c({node:g,message:"React Hook "+h+" received a function whose dependencies are unknown. Pass an inline function instead."});return}c({node:g,message:"React Hook "+h+" has a missing dependency: '"+f.name+"'. Either include it or remove the dependency array.",suggest:[{desc:"Update the dependencies array to be: ["+f.name+"]",fix:function(a){return a.replaceText(m,"["+f.name+"]")}}]})}else"useMemo"!==
+h&&"useCallback"!==h||c({node:g,message:"React Hook "+h+" does nothing when called with only one argument. Did you forget to pass an array of dependencies?"});else c({node:g,message:"React Hook "+h+" requires an effect callback. Did you forget to pass a callback to the hook?"})}}}}}};
Index: frontend/node_modules/eslint-plugin-react-hooks/index.js
===================================================================
--- frontend/node_modules/eslint-plugin-react-hooks/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react-hooks/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,9 @@
+'use strict';
+
+// TODO: this doesn't make sense for an ESLint rule.
+// We need to fix our build process to not create bundles for "raw" packages like this.
+if (process.env.NODE_ENV === 'production') {
+  module.exports = require('./cjs/eslint-plugin-react-hooks.production.min.js');
+} else {
+  module.exports = require('./cjs/eslint-plugin-react-hooks.development.js');
+}
Index: frontend/node_modules/eslint-plugin-react-hooks/package.json
===================================================================
--- frontend/node_modules/eslint-plugin-react-hooks/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/eslint-plugin-react-hooks/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,39 @@
+{
+  "name": "eslint-plugin-react-hooks",
+  "description": "ESLint rules for React Hooks",
+  "version": "4.6.2",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/facebook/react.git",
+    "directory": "packages/eslint-plugin-react-hooks"
+  },
+  "files": [
+    "LICENSE",
+    "README.md",
+    "index.js",
+    "cjs"
+  ],
+  "keywords": [
+    "eslint",
+    "eslint-plugin",
+    "eslintplugin",
+    "react"
+  ],
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/facebook/react/issues"
+  },
+  "engines": {
+    "node": ">=10"
+  },
+  "homepage": "https://reactjs.org/",
+  "peerDependencies": {
+    "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
+  },
+  "devDependencies": {
+    "@typescript-eslint/parser-v2": "npm:@typescript-eslint/parser@^2.26.0",
+    "@typescript-eslint/parser-v3": "npm:@typescript-eslint/parser@^3.10.0",
+    "@typescript-eslint/parser-v4": "npm:@typescript-eslint/parser@^4.1.0",
+    "@typescript-eslint/parser-v5": "npm:@typescript-eslint/parser@^5.0.0-0"
+  }
+}
